/*
    TreeViewer - Cross-platform software to draw phylogenetic trees
    Copyright (C) 2023-2024  Giorgio Bianchini, University of Bristol
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, version 3.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

using Avalonia.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using PhyloTree;
using TreeViewer;
using VectSharp;
using System.Text.Json;
using Avalonia;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.VisualTree;
using VectSharp.PDF;
using VectSharp.SVG;
using VectSharp.Raster.ImageSharp;
using VectSharp.Raster;
using System.Runtime.InteropServices;
using System.Collections;

namespace ExportPDF
{
    /// <summary>
    /// This module is used to export the tree plot in PDF, SVG, PNG or TIFF format.
    /// </summary>

    public static class MyModule
    {
        public const string Name = "Export";
        public const string HelpText = "Exports the tree plot as a PDF, SVG or PNG image.";
        public const string Author = "Giorgio Bianchini";
        public static Version Version = new Version("1.1.3");
        public const string Id = "d5d75840-4a71-4b81-bfc4-431736792abb";
        public const ModuleTypes ModuleType = ModuleTypes.MenuAction;

        public static string ItemText { get; } = "Export";
        public static string ParentMenu { get; } = "File";
        public static string GroupName { get; } = "Save";
        public static Avalonia.AvaloniaProperty PropertyAffectingEnabled { get; } = MainWindow.IsTreeOpenedProperty;
        public static List<(Avalonia.Input.Key, Avalonia.Input.KeyModifiers)> ShortcutKeys { get; } = new List<(Avalonia.Input.Key, Avalonia.Input.KeyModifiers)>() { (Avalonia.Input.Key.P, Avalonia.Input.KeyModifiers.Control), (Avalonia.Input.Key.P, Avalonia.Input.KeyModifiers.Control | Avalonia.Input.KeyModifiers.Shift), (Avalonia.Input.Key.P, Avalonia.Input.KeyModifiers.Control | Avalonia.Input.KeyModifiers.Alt) };
        public static bool TriggerInTextBox { get; } = false;

        public static double GroupIndex { get; } = 1;
        public static bool IsLargeButton { get; } = false;
        public static List<(string, Func<double, VectSharp.Page>)> SubItems { get; } = new List<(string, Func<double, VectSharp.Page>)>() { ("Export PDF", null), ("Export SVG", null), ("Export PNG/TIFF", null) };

        private static string Icon16Base64 = "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACJSURBVDhPpZJREkAwDETLydz/UuR1NgiptuN9yDbZNYTym73PJmsODslAjVrYq9pvmEoGvE8YTa2DJ0wkA/c+4ZZvYbAYOp9kgczXfIInLd+qOgw3Mq59cJL8xH2E0dQwmIEwOerUElOGjTcscr0GV/U/cR8hNDUMeriPapxfoe5Aukv6I/2jlAOd58tO0u+E4gAAAABJRU5ErkJggg==";
        private static string Icon24Base64 = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADOSURBVEhL1ZVRDsIgEESpJ+v9L1V5dIhYXdit8OH7cIdxZ0iaGtNyjji7oj5ISHYp1bm8Ttlj2JbsUvcoRzPLFyPYlOzS7lHOmSnLhi3JLtc9yj3ZjaUto7OJVTbMWkEPnuxDcxn/cwGPK/P5VuFKhmmzlHNmyjrBkQxzzVKOx5Q19wKgHJ9ZjdsXWFBOL9P1Q2NHMs5PYYNc+XpMfMoP8y1LKT6zGtMuoBSPKWveBZRyZso6wZEM02bRmfdywJUM48mW11T6FsP/g7Wk9AQmPT9vEy1f+AAAAABJRU5ErkJggg==";
        private static string Icon32Base64 = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADlSURBVFhH7ZbRFoIgDECxL/PP+zTb6sLJgwgxZiftvsgGbFdTTuHPYmemVBc3rhbuJonXTSwLYRWWKzPXSJ8Emz8WYGyXYGOXgCJDmwSbugUUCfsl2GASUCS1kiBdh/VmAUXSSYJUlSkunoRnpkJr8dZ6I84BG3pHCqEZyjXX+/oTcBfggSjbnyaTbj8BYSSXYMJTYP+QIukmoEhYliAxTKCEtNiWIHAXUKRNJuF2ErZyvpNwD2mTvwcMzvMSUm5VT8LjPkPKpXoyLDdXSHoKvLNurjBxhEDeXEjnwGh+5x/RxQnhASIjhn1oGjSIAAAAAElFTkSuQmCC";

        public static VectSharp.Page GetIcon(double scaling)
        {
            byte[] bytes;

            if (scaling <= 1)
            {

                bytes = Convert.FromBase64String(Icon16Base64);
            }
            else if (scaling <= 1.5)
            {
                bytes = Convert.FromBase64String(Icon24Base64);
            }
            else
            {
                bytes = Convert.FromBase64String(Icon32Base64);
            }

            IntPtr imagePtr = Marshal.AllocHGlobal(bytes.Length);
            Marshal.Copy(bytes, 0, imagePtr, bytes.Length);

            VectSharp.RasterImage icon;

            try
            {
                icon = new VectSharp.MuPDFUtils.RasterImageStream(imagePtr, bytes.Length, MuPDFCore.InputFileTypes.PNG);
            }
            catch (Exception ex)
            {
                throw ex.InnerException;
            }
            finally
            {
                Marshal.FreeHGlobal(imagePtr);
            }

            VectSharp.Page pag = new VectSharp.Page(16, 16);
            pag.Graphics.DrawRasterImage(0, 0, 16, 16, icon);

            return pag;
        }

        public static Avalonia.Controls.Control GetFileMenuPage()
        {
            return new ExportPage();
        }

        public static List<bool> IsEnabled(MainWindow window)
        {
            return new List<bool>() { window.IsTreeOpened, window.IsTreeOpened, window.IsTreeOpened, window.IsTreeOpened };
        }

        public static Task PerformAction(int index, MainWindow window)
        {
            window.RibbonBar.SelectedIndex = 0;
            ExportPage pag = window.GetFilePage<ExportPage>(out int ind);
            window.RibbonFilePage.SelectedIndex = ind;
            ((RibbonFilePageContentTabbedWithButtons)pag.PageContent).SelectedIndex = Math.Max(0, index);

            return Task.CompletedTask;
        }
    }

    class ExportPage : RibbonFilePageContentTemplate
    {
        private static string Icon32PDFBase64 = "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";
        private static string Icon48PDFBase64 = "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";
        private static string Icon64PDFBase64 = "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";

        private static string Icon32SVGBase64 = "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";
        private static string Icon48SVGBase64 = "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";
        private static string Icon64SVGBase64 = "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAHYAAAB2AH6XKZyAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAEVBJREFUeJzNm3tsHMd9xz+7ey/e8Xl8Ha0HTUoi1dCkrFCULVtsnMAV0DpqGxl17aJqDP9TBEUbIC2CFEVSBGmDAkmRpEHi2nBjO43ToAZq11Es27EVS7LEs009otCkRNKiRFIkRVF8HV93tzvTP/Zxe8cleZJJpyONbnZ2dn6/73d+85vfzK4UVkl9fX2VhYWFf6qqvjtBIACfqkoBIKwa8y8+nyqFMJ8TdgGBqvpkdh2oqiKtLphPzM01bK//p9X02MikrHRjdHT0r6LR6L8qqhqUQiKtemn9k7l2XSiK00iSnbKuZeZqenqakWtDvfe07Wm8bRQfIXkSMDAwcGdNTc1FXTeChjVy0kauKEgpHQwOFE/Q2ZWKRZCUGULHx8dJp9OkFhd679n78ZOgelX6fL7Pp3U96DZbkyoFIUzwEosUaQOyLnA/opigrSwBYYOXEqRECgFS4g+GGjre7ezdEJSrJE8CVFXdJIREYOoppP0rkNI9pDjEuC4cKkySZBZYp6WiOBnrOlRQsOPd9zsvbhBWz+RJQNow0g5QxRphKc0LRXGNfGZqSKTTLosOa+RlTjljL9k1gWCo8XT83Y/NEjwJME3fBCqEpabMEGH/ySQly9yzIAkXU1bOthfzWXd3BeHIjtMdHw8JngRIYY7pMh+gWMq61AcbrMwydweskm3utsk7z0qJyDEpBQhHwjtOd8Q3nARvC5DCNG17NN2YbLjOtLDpyHZ2y7Pbcmxi3DlDkLS8bDgS2dERf/fSx06AqqjSHk0ppT31rWTB9QDr9urLzT3zB9vkly8cSClQbIKASCTcEI/HN4yEFS3ARGzmjLPzNnfF1BzAIcbL3N094LYE53m57DkpJKGCgoaOjo4NIWEFJ5hj7q5hyjV3FMVZ26+Pj/Psc886liOl6Ue8TB2XuUs3u+4kBbZvKNggEryngKZm1kDHbN1rO8sCGQUojES4euUqly5dckbxhz/8IefOnV/uFFbwBaotyYo5bHKklIRCoYZ4PL6uccIqy6CpiOf67prCbscXjkRoaWnh9ddfByTvvHOSycmbbN68icRcgrfeepN4vMN8xKJY07SseFzKbAdr19m/fr+/8fTp0+tGgicBipqxAK/1Xbhi4WvDw7z15pv09/WRTqXYv38/N27c4EznGY4ePcrOnTt57bXX+PrXv05XVxfBYDDL5H2ahkOny+S9CLFzIBBoPHXq1LqQ4LkZ6u2/8m9l5dG/tjRYtYOBgQGOHTvG6OgoiUSCWCzGzMwMiUQCKQShUJDGxkYaGhuor6ujrKyMH/3oR3xy92727dtHKpnk8sBlwuHwMuBuAnLLAKlU6tL999+/86MQ4POqtGN2wBULeBOxafMmDh78LAsLC0xMTDA+Ps7Y2BhdXV3cd98+mpubqaqqoqysDFVR+Ol//ZSpyUm2bdsGQCAQwO/zMzM9TTgcRtO0ZSOOh3wpJZqmNZ48efLX7e3tu9aXANU1FtJcm3OT7eQC/gBVVdUA1N1Zx9LSEnPzc3R1ddHe3k5NTY3lPyS//OUbnOnspL19P+++G+fqlSvs3buX1tZWBgcHmZiYIJ1Oo+s6Qgh0XXdWEifocm2eVFXF5/O1SCkVRVFWN9VbIsBeB3PA2imZTBIIBHLqzfahUJCCghDBQIB0OoXt6U+fOs2RI0dASnq6eyivKCcWi1FZWYmqqtTW1lJTU8Pi4iK6rjvZMAyEEA4JbgI0TUNVPd3YRyMA1GVLsnuRfv7Hz/PJ3btpa2tz3VeyHEowGGQuMYcUklePvsrJEydobm6mp7uboqJCPvPpT1NTU0M4HHZM3e/3oyiKA9wmQQiBYRgZTaREVVXHCtadAIXMkU9mlDPwKsoreP/9Ttr2tPH000+TSCRo2dVCS3MzsVg1UkqCwQAzM9O88MJP6Ovt5eGHD7Fj+3au7mklHo/T0dHB4cOHl81zy6wzuigKhmFkBU22JXhHmutAACru4D8nSe6+exdPPvkkC4sLPPDAp+jp6aH30iV++frrlJaV0tLSghSCl156CVVVad+/H8MwOH/+PDU1NRw6dAjDMJBSkkqlEEIQCASywLlJcCTLnB3qOiRPAoTIrMXmptCedwAKtbW1RKNRLly4wL5772Xr1q3MzMwwPDTEiRMneOONN5zYMVxQwNmzZwiFQkQiEVRVpaGhwQH7yiuvIKXk0KFDyzy9pmnZ+wKLHJuEjbMAkelcCMHLL79MMBikrKyMaFkZpaWlbKuvp7PzfWpi1XR3d/PBBx9wbXiYWCxGW2srM7MzaKrKgQMH8Pv9+Hw+NE2jsLDQAZ9Op4nH4zzyyCPLIj+31/f5fKiq6viBZXuI9SZA1RRpOz1VVVBVhYmJG1y+/CFziQTT09Mkk0mklDz1709xZ92dNDY0EC0rIxQK8YcHD9LR0cGvf32eHTt2rBjMnD17lnA4TFNTk3Mvt417Wmia5tS5l8Z1J0BI80jMNrAHH3yQpaUl8/g6meTUqVOcO3eO/fffT1PTJygpKaG0tITvfve7fHL3bkpKiqmurmJqasozoJFSMjU1xbFjx7jrrruc8Hit6M/t/JyDk40gAMvPSEBBUhiJUBiJMD8/z49f+h9u3LjBww8f4u5duxgZGaE8GkVIwbVr13js0UeRUlJdXc3c3By6rqOqKqlUir6+Pnp6euju7mZycpLi4mLi8TihUIj29nYzHPYAvRYp606AoqnS9i+2HQwODvLMM89QWVHBo48+yrZt20inUrzwwgvMz81RW1tLwO8nkUhw8eJFR7kjR44wNDREf38/xcXFbNmyhd27d7NlyxaKioro7++nq6uLY8eOsX//fh588EGCweCaoDeUAHKWmq6uLp5//jn2tLbywAMPEIvFUBTwaSqPf/4v6Ovr4+2330bTNF5++WVSqRS6rgNw6dIlamtr2bVrF3fccQeRSISioiJn2aupqWHnzp309vbS3d3N8PAw9fX1v10CsjZDUuL3+3jooYdo27OHwsJCq970xrW1tZSWlvLzn/+cxx57jOpqMxAyDINnn32We++9l127dhGJRLIUtkH4fD5isRjl5eU0NDRQXFycF+gNJUDz+aTduQLU19VTX19PwO+3GbJORUxFdF2ntbWVu+++G7/f7yioqiqRSCQL/EogfD4fVVVVtwx+QwgwlxhhelsgEPBn3oBJ6WyPbeHFxcUcPHgwCzyQFbTcKqDfKgE+TZE2eHeS0hUhuoSrqkpRUdGyJQ9w1uuNIuCjphXiAAeyGRHagm5RKTt6u1UC4vE4Pp+P1tbWDQUPK5wJIqS1H8regeWTVwKVL/i5uTmOHDnieTK0Wl/rSoCiKVJKmXmxmYfidnLX21vZWyHwtddeIxaL0djY6NQtLi56ytkwH4D1MuNW5uHPfvYz6urq2LNnj2M5thPMt5/x8XHee+892tvbOXHiBMPDwwwODiKl5Ktf/eoy8G7560sA+ZuufV1RUcGrr77K8ePH+dznPkddXR2Ac66Xq7Cu64yPjzM6Osro6ChjY2MMDQ0hhODChQvEYjGi0Sj19fVUV1dnPfv/ggB3O4D77ruPzZs309nZyVNPPUVTUxOpVAopzY3P2NgYIyMjDuDr16/j8/morKw0t9nRKJs2baKrq4uFhQXa2trYunUroVAIVVU3zBl6vxfo7f1WNBr9u3zA57aZnZ2lv7+f9957j97eXmc6lJeXE41GKSkpobi4mDLrXKGgoIBAIEAwGCQYDDI+Ps6pU6e4cOECBw4cYN++fcv2/7nl5uZm9XZPhT0J6Onp+XZFRcXf5uPo1qv8UeS0tLTcNgFrTgG3Iqsqoy8SXOiF9IK1cCwH59XHSm1gbfn29dL/fvnw4ku/t5yA3MNtVQpDyK7CP3qrC2vTvyoB+YInNUNs6Dv49GkTuCuQyvqVOdeZjl31q93z6gsI8TyoK7d196Uhp/67/Rtlj5z8BqCveKjuBXalHJ48gS89iflxkQBp5GSxQrbuIzza5dNHThuE1ddK7QRIQykJq1/5/d2FZYDqaQGGYSirEZFbVtNTlgJrjBqYu8jyVohsBX8RpGdguhumuqGsyaxfGIbJC9l9xdrBXwjjcUjetEa+Gir3QkEVGEuwcA0mzkBy2lu+NC1MgcCBttiWo+f6p/KeAiuBN19bWeyuZb6+MLT9CxRty2538yy8//dQUANNfwOJy3DqLzP3fWFo+YpZHjlmytrxOGz7M1C0bJH9/4m8+IwjV+bqICVSQmI+qQHKigSsBnqZg5QyhwCPeQpQ9ycm+MnfIHv/A5JTEKqEQDFSChjvQJUCiuoQgTLk4gQgUSpbUFUf8kYnxtIsau1B1O2HQaQRfT9BTJwBNYhSsgOSNxHCsAY7Wx9HV0BIqcA6RIJ2WQjD/bR1P5sINbwZBRA3zyEmfmPeSgxllFy8iTLVjRK9CxFtRQweBSS+cvMdpDH6DoaQaDsOA5A+/22Mq0ec/uXIO7kgPAMld5WnE9R1XVnL8bmzkAJDGBiG/VbXwDAEhiHQdQPdMLOY7jOFbv9zlOYvIYq2Y+gGhm6Y7XQDffQkAEplm1OnVO0FJOlrxxEFm1EKqpD6IqmBX5gyrP4NVzblCgwhnawbZjYy+/3844DVyoYuMHQj8zWZlNZqmM2+fvGnhEp3olXfg1b7EFrtQxiT3STPfRsxbX4AJq+dwN/0BdTKPRhCoBZtRQnHMKZ6Sc+O4IvdA4CYH0HXU6hFtRT9wYuOjOTFn7B4/nueo+2uM6xjzzWXwfyy+fraHk1DzxkJ+11/ap75E19i/vgXSQ+9CSKNFv0EBb/7fUSgAl3XSU1dRiSGUALFULITtcoEnL52HEOYVmWaiA/DkBhLsyz1v0R6/KxJjAkO3TCzITLZywLWhQBhCOudvpl1wyBtma9jlq6PHpIjcRLv/APTv3gUsXQTxR9Bi91nTRlBcvg4AFrsXjRrxBcHj6MbkvTciKl4JIahFZBauMls/J9ZuvwLR28bpNvszTqLCNep/6oEuIlYrawLc76ldTPrWRagO3NZ4DPrrJyauYoxOwjgKGsIydLQCQD8m9rxV+7GmL9OcuIShoDk9BB6YghFC1LQ9IQzsrZhmBaQPd+XEWGs4QMMw1DyJcBcAYTzIsS6ac09VxgNVD70HEJfQJ/+EKkv4Y824K/ajUwvsDh4HMP63yj66DlKkjP4o+YHYIt9r6DbCJHMvP89yj/zLQrvepxAdRupm90Eyn/HJMAC61Jl+eC6ynkFQmsRYI/2Sg7HTvriTUJ37CVQ2eLUJcfOMN35fZKJ666nDBaHThLZ/lkAFq4ed+atBBIDxzDe+jLRti8SqGwiUGm+Xdbnxlia6EF3r8gO6Gzl8ooDvEB71Qmp5sX62NEvoAaK0AqioPrQE6OI9EKOXPP3+q++Br/6mqfyALMfvsnsh2/ii1SjBosxFifRFybJjUa9dZEsLAp9VQK8gHqVFUUhHanHd/3YisLcyViaJbU46yFr9WuvvgD02TFgLI9nXToYYvQ7r4yOAHJVH7Bq+OsIk+gV9zM90Udo9gyKSHp1ueb08FLULWO1ZxWRnFu5v+zKxbQcfvuD+X8EFgCxkgWkVyPArZSUEk3zoW8/zHjij50vR/LaQ7j6ykeOV3shhPH444/vWlpa8uJuOR+gA/PA3IoE3Lx582JZWVleSklpHn8HAgFKS0tv6Rj8dolxl5PJ5PDS0tJwHuDdJAjAgBV8wDe/+c1Xf/CDHwwEg8G6fJUC81XYWie4a5Xd8vJ57sqVK88BqVsgICtpXpWDg4NLyWTyYktLS0MgEKgB8o4LvMq3C361shBibmBg4Mknnnjie5jmfFtptQ/tCoG65ubmsra2tvLbFbBWEkLk9bGftNZtgIWFhfSLL754DZgABvH2nXmltYSrQBjw366ADUoSWAS8l5xbSP8HLBQjXm7rOEIAAAAASUVORK5CYII=";

        private static string Icon32PNGBase64 = "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";
        private static string Icon48PNGBase64 = "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";
        private static string Icon64PNGBase64 = "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";

        private static string Icon16SaveBase64 = "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAB1SURBVDhPYywsLGxgYGCoB2Iw6O/vZ4Qy4QCo5j+IBsnB2EDgCOQfYAIy4JpJBPuBhjmADKAE7CfKAJDTsXkNBDAMADkLysQA2OSQA4UsQGkYUG4AihdwBRQ6QNZDsQtYoDQcEApUdFeOegHihUYIkxzA0AgAg2E1fE0CEN8AAAAASUVORK5CYII=";
        private static string Icon24SaveBase64 = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACmSURBVEhL7ZQNCoAgDIVXdLA6kicoT+CR6ma1lYU214+0IOgDacjcY71pYYzpAKDFFeGcK3zIwDOjD6O8YL/B/YGCEhcr/gA9itUUkIAWs4imANFrCwCZvBkWcmSyRKqW2AEm03RdRsoXO3gKdQ9+gVNSJlsc0VsTtOInKXp6WAe5xYnUWfVfVPkv4+79kG7++ybnvEEh+3rfvwe/yYyUyXYJNQA7AbQ0QK1sxcbyAAAAAElFTkSuQmCC";
        private static string Icon32SaveBase64 = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADBSURBVFhH7ZVtCoMwDIbjTqa/t8v0BK4n6GX2X2/mkhqGX41FLOkgDxReKW1e0iQ2zrk3APS4DgkhNCxF8J6JZWR5brPX4d7IGh64ksELMaChlnU0oMHPhJYBIprQNEAM2gaAumBVvVtyu0BCiqGegVMD6P7D8hJ4nuZMktMnKE39T1AaM2AGpDb0OAXFHs6FZ8lz/lqTzMBdwQm868Vyh9VAsgaWf8Gr4zrnjv/IwB1UmwEzYF1gXVCFAT9LDcB/AdJlT6191EDNAAAAAElFTkSuQmCC";

        private static VectSharp.Page GetSaveIcon(double scaling)
        {
            return GetIcon(scaling, ref Icon16SaveBase64, ref Icon24SaveBase64, ref Icon32SaveBase64, 16);
        }

        private static VectSharp.Page GetPDFIcon(double scaling)
        {
            return GetIcon(scaling, ref Icon32PDFBase64, ref Icon48PDFBase64, ref Icon64PDFBase64, 16);
        }

        private static VectSharp.Page GetSVGIcon(double scaling)
        {
            return GetIcon(scaling, ref Icon32SVGBase64, ref Icon48SVGBase64, ref Icon64SVGBase64, 16);
        }

        private static VectSharp.Page GetPNGIcon(double scaling)
        {
            return GetIcon(scaling, ref Icon32PNGBase64, ref Icon48PNGBase64, ref Icon64PNGBase64, 16);
        }

        private static VectSharp.Page GetIcon(double scaling, ref string icon32, ref string icon48, ref string icon64, double resolution)
        {
            byte[] bytes;

            if (scaling <= 1)
            {

                bytes = Convert.FromBase64String(icon32);
            }
            else if (scaling <= 1.5)
            {
                bytes = Convert.FromBase64String(icon48);
            }
            else
            {
                bytes = Convert.FromBase64String(icon64);
            }

            IntPtr imagePtr = Marshal.AllocHGlobal(bytes.Length);
            Marshal.Copy(bytes, 0, imagePtr, bytes.Length);

            VectSharp.RasterImage icon;

            try
            {
                icon = new VectSharp.MuPDFUtils.RasterImageStream(imagePtr, bytes.Length, MuPDFCore.InputFileTypes.PNG);
            }
            catch (Exception ex)
            {
                throw ex.InnerException;
            }
            finally
            {
                Marshal.FreeHGlobal(imagePtr);
            }

            VectSharp.Page pag = new VectSharp.Page(resolution, resolution);
            pag.Graphics.DrawRasterImage(0, 0, resolution, resolution, icon);

            return pag;
        }


        public ExportPage() : base("Export")
        {
            RibbonFilePageContentTabbedWithButtons content = new RibbonFilePageContentTabbedWithButtons(new List<(string, string, Control, Control)>()
            {
                ("Export PDF", "Publication-ready vector format", new DPIAwareBox(GetPDFIcon) { Width = 32, Height = 32 }, GetPDFPage()),
                ("Export SVG", "Vector format useful for further editing",  new DPIAwareBox(GetSVGIcon) { Width = 32, Height = 32 }, GetSVGPage()),
                ("Export PNG/TIFF", "Raster formats for easy dissemination",  new DPIAwareBox(GetPNGIcon) { Width = 32, Height = 32 }, GetPNGPage()),
            });

            this.PageContent = content;

            this.PropertyChanged += (s, e) =>
            {
                if (e.Property == ExportPage.IsVisibleProperty)
                {
                    content.SelectedIndex = 0;
                }
            };
        }

        private Control GetPDFPage()
        {
            Grid mainContainer = new Grid() { Margin = new Avalonia.Thickness(25, 0, 0, 0) };
            mainContainer.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            mainContainer.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            mainContainer.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            mainContainer.RowDefinitions.Add(new RowDefinition(1, GridUnitType.Star) { MaxHeight = 320 });
            mainContainer.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));

            mainContainer.Children.Add(new TextBlock() { FontSize = 20, Foreground = new SolidColorBrush(Color.FromRgb(0, 114, 178)), Text = "Export PDF" });

            StackPanel descriptionPanel = new StackPanel();

            {
                descriptionPanel.Children.Add(new TextBlock()
                {
                    FontSize = 12,
                    Foreground = new SolidColorBrush(Color.FromRgb(102, 102, 102)),
                    TextWrapping = TextWrapping.Wrap,
                    Text = "The PDF format, introduced by Adobe in 1993 and standardised in 2008, enjoys widespread compatibility and is usually the format of choice for publication-ready figures.",
                    Margin = new Avalonia.Thickness(0, 0, 0, 5)
                });
            }

            Grid.SetRow(descriptionPanel, 1);
            mainContainer.Children.Add(descriptionPanel);

            Grid regionRow = new Grid();
            regionRow.ColumnDefinitions.Add(new ColumnDefinition(0, GridUnitType.Auto) { MinWidth = 150, MaxWidth = 200 });
            regionRow.ColumnDefinitions.Add(new ColumnDefinition(1, GridUnitType.Star));

            ComboBox cropRegionBox;
            List<Rectangle> cropRegionRects;

            {
                TextBlock blk = new TextBlock() { Text = "Crop region:", Margin = new Thickness(0, 0, 10, 10), VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, FontSize = 14 };

                List<string> items = new List<string>() { "Entire plot" };

                cropRegionRects = new List<Rectangle>();

                cropRegionBox = new ComboBox() { Items = items, SelectedIndex = 2, Margin = new Thickness(0, 0, 0, 10), VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, MinWidth = 150, FontSize = 14 };

                Grid.SetColumn(cropRegionBox, 1);
                regionRow.Children.Add(blk);
                regionRow.Children.Add(cropRegionBox);

                Grid.SetRow(regionRow, 2);
                mainContainer.Children.Add(regionRow);
            }

            regionRow.IsVisible = cropRegionRects.Count > 0;

            this.PropertyChanged += (s, e) =>
            {
                if (e.Property == ExportPage.IsVisibleProperty)
                {
                    List<string> items = new List<string>() { "Entire plot" };

                    cropRegionRects = new List<Rectangle>();

                    InstanceStateData stateData = this.FindAncestorOfType<MainWindow>()?.StateData;

                    if (stateData != null && stateData.Tags.TryGetValue("5a8eb0c8-7139-4583-9e9e-375749a98973", out object cropRegionsObject) && cropRegionsObject != null && cropRegionsObject is Dictionary<string, (string, Rectangle)> cropRegions)
                    {
                        foreach (KeyValuePair<string, (string, Rectangle)> kvp in cropRegions)
                        {
                            if (IsAlive(kvp.Key, this.FindAncestorOfType<MainWindow>()))
                            {
                                items.Add(kvp.Value.Item1);
                                cropRegionRects.Add(kvp.Value.Item2);
                            }
                        }
                    }

                    cropRegionBox.Items = items;
                    cropRegionBox.SelectedIndex = 0;
                    regionRow.IsVisible = cropRegionRects.Count > 0;
                }
            };

            ScrollViewer pageLayoutContainer = new ScrollViewer() { HorizontalScrollBarVisibility = Avalonia.Controls.Primitives.ScrollBarVisibility.Disabled, VerticalScrollBarVisibility = Avalonia.Controls.Primitives.ScrollBarVisibility.Auto, AllowAutoHide = false, Padding = new Avalonia.Thickness(0, 0, 17, 0) };
            Grid.SetRow(pageLayoutContainer, 3);
            mainContainer.Children.Add(pageLayoutContainer);

            Grid pageLayoutGrid = new Grid() { VerticalAlignment = Avalonia.Layout.VerticalAlignment.Top };
            pageLayoutGrid.ColumnDefinitions.Add(new ColumnDefinition(1, GridUnitType.Star));
            pageLayoutGrid.ColumnDefinitions.Add(new ColumnDefinition(1, GridUnitType.Star));
            pageLayoutGrid.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            pageLayoutGrid.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            pageLayoutGrid.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            pageLayoutGrid.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            pageLayoutGrid.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            pageLayoutGrid.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            pageLayoutGrid.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            pageLayoutGrid.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            pageLayoutContainer.Content = pageLayoutGrid;

            pageLayoutGrid.Children.Add(new TextBlock() { Text = "Page layout", FontSize = 16, HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Left, FontWeight = FontWeight.Bold });

            ComboBox units = new ComboBox() { VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, Padding = new Avalonia.Thickness(5, 2, 5, 2), FontSize = 13, Margin = new Avalonia.Thickness(5, 0, 0, 0) };

            {
                Grid grd = new Grid() { Margin = new Avalonia.Thickness(0, 0, 0, 0) };
                grd.ColumnDefinitions.Add(new ColumnDefinition(1, GridUnitType.Star));
                grd.ColumnDefinitions.Add(new ColumnDefinition(0, GridUnitType.Auto));
                grd.ColumnDefinitions.Add(new ColumnDefinition(0, GridUnitType.Auto));

                grd.Children.Add(new TextBlock() { Text = "Page size:", FontSize = 13, HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Left, VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center });

                TextBlock blk = new TextBlock() { Text = "Units:", FontSize = 13, VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center };
                Grid.SetColumn(blk, 1);
                grd.Children.Add(blk);

                List<string> items = new List<string>() { "cm", "mm", "in" };
                units.Items = items;
                units.SelectedIndex = 0;

                Grid.SetColumn(units, 2);
                grd.Children.Add(units);

                Grid.SetRow(grd, 1);
                pageLayoutGrid.Children.Add(grd);
            }

            {
                TextBlock blk = new TextBlock() { Text = "Preview:", FontSize = 13, VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, Margin = new Avalonia.Thickness(10, 0, 0, 0) };
                Grid.SetRow(blk, 1);
                Grid.SetColumn(blk, 1);
                pageLayoutGrid.Children.Add(blk);
            }

            List<(string, string, double, double)> PageSizes = new List<(string, string, double, double)>()
            {
                ("A0", "841 x 1189 mm", 2383.937008, 3370.393701),
                ("A1", "594 x 841 mm", 1683.779528, 2383.937008),
                ("A2", "420 x 594 mm", 1190.551181, 1683.779528),
                ("A3", "297 x 420 mm", 841.8897638, 1190.551181),
                ("A4", "210 x 297 mm", 595.2755906, 841.8897638),
                ("A5", "148 x 210 mm", 419.5275591, 595.2755906),
                ("A6", "105 x 148 mm", 297.6377953, 419.5275591),
                ("A7", "74 x 105 mm", 209.7637795, 297.6377953),
                ("A8", "52 x 74 mm", 147.4015748, 209.7637795),
                ("B0", "1000 x 1414 mm", 2834.645669, 4008.188976),
                ("B1", "707 x 1000 mm", 2004.094488, 2834.645669),
                ("B2", "500 x 707 mm", 1417.322835, 2004.094488),
                ("B3", "353 x 500 mm", 1000.629921, 1417.322835),
                ("B4", "250 x 353 mm", 708.6614173, 1000.629921),
                ("B5", "176 x 250 mm", 498.8976378, 708.6614173),
                ("B6", "125 x 176 mm", 354.3307087, 498.8976378),
                ("B7", "88 x 125 mm", 249.4488189, 354.3307087),
                ("B8", "62 x 88 mm", 175.7480315, 249.4488189),
                ("B9", "44 x 62 mm", 124.7244094, 175.7480315),
                ("B10", "31 x 44 mm", 87.87401575, 124.7244094),
                ("Letter", "8.5 x 11 in", 612, 792),
                ("Legal", "8.5 x 14 in", 612, 1008),
                ("Ledger", "11 x 17 in", 792, 1224),
                ("Executive", "7.25 x 10.55 in", 522, 759.6),
                ("ANSI C", "17 x 22 in", 1224, 1584),
                ("ANSI D", "22 x 34 in", 1584, 2448),
                ("ANSI E", "34 x 44 in", 2448, 3168),
            };

            ListBox pageSizeBox = new ListBox() { Background = Brushes.White, BorderBrush = new SolidColorBrush(Color.FromRgb(198, 198, 198)), BorderThickness = new Avalonia.Thickness(1, 1, 1, 1), Margin = new Avalonia.Thickness(0, 5, 0, 0), Height = 140, VirtualizationMode = ItemVirtualizationMode.None };
            Grid.SetRow(pageSizeBox, 2);
            pageLayoutGrid.Children.Add(pageSizeBox);


            {
                List<Grid> pageSizeItems = new List<Grid>();

                {
                    Grid grd = new Grid() { Margin = new Avalonia.Thickness(2, 0, 2, 0) };
                    grd.Children.Add(new TextBlock() { FontSize = 13, Text = "Custom" });
                    pageSizeItems.Add(grd);
                }

                for (int i = 0; i < PageSizes.Count; i++)
                {
                    Grid grd = new Grid() { Margin = new Avalonia.Thickness(2, 0, 2, 0) };
                    grd.ColumnDefinitions.Add(new ColumnDefinition(70, GridUnitType.Pixel));
                    grd.ColumnDefinitions.Add(new ColumnDefinition(1, GridUnitType.Star));

                    grd.Children.Add(new TextBlock() { FontSize = 13, Text = PageSizes[i].Item1 });

                    TextBlock blk = new TextBlock() { FontSize = 13, Text = PageSizes[i].Item2 };
                    Grid.SetColumn(blk, 1);
                    grd.Children.Add(blk);
                    pageSizeItems.Add(grd);
                }

                pageSizeBox.Items = pageSizeItems;
                pageSizeBox.SelectedIndex = 0;
            }

            ContentControl previewControl = new ContentControl() { Width = 140, Height = 140, BorderThickness = new Avalonia.Thickness(0), HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center, VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center };
            Grid.SetRow(previewControl, 2);
            Grid.SetColumn(previewControl, 1);
            pageLayoutGrid.Children.Add(previewControl);

            NumericUpDown widthNud = new NumericUpDown() { Margin = new Avalonia.Thickness(5, 0, 0, 0), Padding = new Avalonia.Thickness(2, 0, 2, 0), VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, Minimum = 0, Maximum = 508, FormatString = "{0:0.##}", FontSize = 13, Increment = 0.01 };
            NumericUpDown heightNud = new NumericUpDown() { Margin = new Avalonia.Thickness(5, 0, 0, 0), Padding = new Avalonia.Thickness(2, 0, 2, 0), VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, Minimum = 0, Maximum = 508, FormatString = "{0:0.##}", FontSize = 13, Increment = 0.01 };

            NumericUpDown topNud = new NumericUpDown() { Margin = new Avalonia.Thickness(5, 0, 0, 0), Padding = new Avalonia.Thickness(2, 0, 2, 0), VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, FormatString = "{0:0.##}", FontSize = 13, Increment = 0.01 };
            NumericUpDown bottomNud = new NumericUpDown() { Margin = new Avalonia.Thickness(5, 0, 0, 0), Padding = new Avalonia.Thickness(2, 0, 2, 0), VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, FormatString = "{0:0.##}", FontSize = 13, Increment = 0.01 };

            NumericUpDown leftNud = new NumericUpDown() { Margin = new Avalonia.Thickness(5, 0, 0, 0), Padding = new Avalonia.Thickness(2, 0, 2, 0), VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, FormatString = "{0:0.##}", FontSize = 13, Increment = 0.01 };
            NumericUpDown rightNud = new NumericUpDown() { Margin = new Avalonia.Thickness(5, 0, 0, 0), Padding = new Avalonia.Thickness(2, 0, 2, 0), VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, FormatString = "{0:0.##}", FontSize = 13, Increment = 0.01 };

            TextBlock widthUnit = new TextBlock() { Text = "cm", FontSize = 13, VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, Margin = new Avalonia.Thickness(3, 0, 0, 0) };
            TextBlock heightUnit = new TextBlock() { Text = "cm", FontSize = 13, VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, Margin = new Avalonia.Thickness(3, 0, 0, 0) };
            TextBlock leftUnit = new TextBlock() { Text = "cm", FontSize = 13, VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, Margin = new Avalonia.Thickness(3, 0, 0, 0) };
            TextBlock topUnit = new TextBlock() { Text = "cm", FontSize = 13, VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, Margin = new Avalonia.Thickness(3, 0, 0, 0) };
            TextBlock rightUnit = new TextBlock() { Text = "cm", FontSize = 13, VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, Margin = new Avalonia.Thickness(3, 0, 0, 0) };
            TextBlock bottomUnit = new TextBlock() { Text = "cm", FontSize = 13, VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, Margin = new Avalonia.Thickness(3, 0, 0, 0) };

            {
                Grid grd = new Grid() { Margin = new Avalonia.Thickness(0, 5, 0, 0), VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center };
                grd.ColumnDefinitions.Add(new ColumnDefinition(50, GridUnitType.Pixel));
                grd.ColumnDefinitions.Add(new ColumnDefinition(1, GridUnitType.Star));
                grd.ColumnDefinitions.Add(new ColumnDefinition(0, GridUnitType.Auto));
                grd.Children.Add(new TextBlock() { Text = "Width:", FontSize = 13, VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center });

                Grid.SetColumn(widthNud, 1);
                grd.Children.Add(widthNud);

                Grid.SetColumn(widthUnit, 2);
                grd.Children.Add(widthUnit);

                Grid.SetRow(grd, 3);
                pageLayoutGrid.Children.Add(grd);
            }

            {
                Grid grd = new Grid() { Margin = new Avalonia.Thickness(0, 5, 0, 0), VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center };
                grd.ColumnDefinitions.Add(new ColumnDefinition(50, GridUnitType.Pixel));
                grd.ColumnDefinitions.Add(new ColumnDefinition(1, GridUnitType.Star));
                grd.ColumnDefinitions.Add(new ColumnDefinition(0, GridUnitType.Auto));
                grd.Children.Add(new TextBlock() { Text = "Height:", FontSize = 13, VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center });

                Grid.SetColumn(heightNud, 1);
                grd.Children.Add(heightNud);

                Grid.SetColumn(heightUnit, 2);
                grd.Children.Add(heightUnit);

                Grid.SetRow(grd, 4);
                pageLayoutGrid.Children.Add(grd);
            }

            RadioButton horizontalRadio = new RadioButton() { Content = "Horizontal", VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, GroupName = "Orientation", FontSize = 13, Padding = new Avalonia.Thickness(3, 0, 0, 0) };
            RadioButton verticalRadio = new RadioButton() { Content = "Vertical", VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, GroupName = "Orientation", IsChecked = true, FontSize = 13, Padding = new Avalonia.Thickness(3, 0, 0, 0) };

            {
                Grid grd = new Grid() { Margin = new Avalonia.Thickness(0, 5, 0, 0), VerticalAlignment = Avalonia.Layout.VerticalAlignment.Top };
                grd.ColumnDefinitions.Add(new ColumnDefinition(0, GridUnitType.Auto));
                grd.ColumnDefinitions.Add(new ColumnDefinition(1, GridUnitType.Star));
                grd.ColumnDefinitions.Add(new ColumnDefinition(0, GridUnitType.Auto));

                Grid.SetColumn(horizontalRadio, 0);
                grd.Children.Add(horizontalRadio);

                Grid.SetColumn(verticalRadio, 2);
                grd.Children.Add(verticalRadio);

                Grid.SetRow(grd, 5);
                Grid.SetRowSpan(grd, 2);
                pageLayoutGrid.Children.Add(grd);
            }

            {
                TextBlock blk = new TextBlock() { Text = "Margins:", FontSize = 13, HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Left, VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, Margin = new Avalonia.Thickness(10, 5, 0, 0) };
                Grid.SetColumn(blk, 1);
                Grid.SetRow(blk, 3);
                pageLayoutGrid.Children.Add(blk);
            }

            {
                Grid grd = new Grid() { Margin = new Avalonia.Thickness(10, 2, 0, 0) };
                grd.ColumnDefinitions.Add(new ColumnDefinition(50, GridUnitType.Pixel));
                grd.ColumnDefinitions.Add(new ColumnDefinition(1, GridUnitType.Star));
                grd.ColumnDefinitions.Add(new ColumnDefinition(0, GridUnitType.Auto));
                grd.Children.Add(new TextBlock() { Text = "Left:", FontSize = 13, VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center });

                Grid.SetColumn(leftNud, 1);
                grd.Children.Add(leftNud);

                Grid.SetColumn(leftUnit, 2);
                grd.Children.Add(leftUnit);

                Grid.SetRow(grd, 4);
                Grid.SetColumn(grd, 1);
                pageLayoutGrid.Children.Add(grd);
            }

            {
                Grid grd = new Grid() { Margin = new Avalonia.Thickness(10, 2, 0, 0) };
                grd.ColumnDefinitions.Add(new ColumnDefinition(50, GridUnitType.Pixel));
                grd.ColumnDefinitions.Add(new ColumnDefinition(1, GridUnitType.Star));
                grd.ColumnDefinitions.Add(new ColumnDefinition(0, GridUnitType.Auto));
                grd.Children.Add(new TextBlock() { Text = "Top:", FontSize = 13, VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center });

                Grid.SetColumn(topNud, 1);
                grd.Children.Add(topNud);

                Grid.SetColumn(topUnit, 2);
                grd.Children.Add(topUnit);

                Grid.SetRow(grd, 5);
                Grid.SetColumn(grd, 1);
                pageLayoutGrid.Children.Add(grd);
            }

            {
                Grid grd = new Grid() { Margin = new Avalonia.Thickness(10, 2, 0, 0) };
                grd.ColumnDefinitions.Add(new ColumnDefinition(50, GridUnitType.Pixel));
                grd.ColumnDefinitions.Add(new ColumnDefinition(1, GridUnitType.Star));
                grd.ColumnDefinitions.Add(new ColumnDefinition(0, GridUnitType.Auto));
                grd.Children.Add(new TextBlock() { Text = "Right:", FontSize = 13, VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center });

                Grid.SetColumn(rightNud, 1);
                grd.Children.Add(rightNud);

                Grid.SetColumn(rightUnit, 2);
                grd.Children.Add(rightUnit);

                Grid.SetRow(grd, 6);
                Grid.SetColumn(grd, 1);
                pageLayoutGrid.Children.Add(grd);
            }

            {
                Grid grd = new Grid() { Margin = new Avalonia.Thickness(10, 2, 0, 0) };
                grd.ColumnDefinitions.Add(new ColumnDefinition(50, GridUnitType.Pixel));
                grd.ColumnDefinitions.Add(new ColumnDefinition(1, GridUnitType.Star));
                grd.ColumnDefinitions.Add(new ColumnDefinition(0, GridUnitType.Auto));
                grd.Children.Add(new TextBlock() { Text = "Bottom:", FontSize = 13, VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center });

                Grid.SetColumn(bottomNud, 1);
                grd.Children.Add(bottomNud);

                Grid.SetColumn(bottomUnit, 2);
                grd.Children.Add(bottomUnit);

                Grid.SetRow(grd, 7);
                Grid.SetColumn(grd, 1);
                pageLayoutGrid.Children.Add(grd);
            }

            StackPanel saveButtonContent = new StackPanel() { Orientation = Orientation.Horizontal, VerticalAlignment = VerticalAlignment.Center };
            saveButtonContent.Children.Add(new DPIAwareBox(GetSaveIcon) { Width = 16, Height = 16, VerticalAlignment = VerticalAlignment.Center });
            saveButtonContent.Children.Add(new TextBlock() { Text = "Export PDF", Margin = new Thickness(5, 0, 0, 0), VerticalAlignment = VerticalAlignment.Center, Foreground = Brushes.Black });

            Button saveButton = new Button() { Content = saveButtonContent, HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center, Margin = new Avalonia.Thickness(0, 5, 0, 0) };
            saveButton.Classes.Add("SideBarButton");
            Grid.SetRow(saveButton, 4);
            mainContainer.Children.Add(saveButton);

            bool suppressChange = false;

            pageSizeBox.SelectionChanged += (s, e) =>
            {
                if (!suppressChange)
                {
                    suppressChange = true;
                    double unitMultiplier = units.SelectedIndex == 0 ? 72 / 2.54 : units.SelectedIndex == 1 ? 72 / 25.4 : 72;
                    int index = pageSizeBox.SelectedIndex;

                    if (index > 0)
                    {
                        if (horizontalRadio.IsChecked == true)
                        {
                            widthNud.Value = PageSizes[index - 1].Item4 / unitMultiplier;
                            heightNud.Value = PageSizes[index - 1].Item3 / unitMultiplier;
                        }
                        else
                        {
                            widthNud.Value = PageSizes[index - 1].Item3 / unitMultiplier;
                            heightNud.Value = PageSizes[index - 1].Item4 / unitMultiplier;
                        }

                        double width = widthNud.Value * unitMultiplier;
                        double height = heightNud.Value * unitMultiplier;

                        MainWindow parent = this.FindAncestorOfType<MainWindow>();

                        if (parent != null)
                        {
                            double plotWidth = parent.PlotBottomRight.X - parent.PlotOrigin.X + 20;
                            double plotHeight = parent.PlotBottomRight.Y - parent.PlotOrigin.Y + 20;

                            if (cropRegionBox.SelectedIndex > 0)
                            {
                                plotWidth = cropRegionRects[cropRegionBox.SelectedIndex - 1].Size.Width;
                                plotHeight = cropRegionRects[cropRegionBox.SelectedIndex - 1].Size.Height;
                            }

                            if (plotWidth > 0 && plotHeight > 0)
                            {
                                double clientWidth = width * 0.9;
                                double clientHeight = height * 0.9;

                                double scale2 = Math.Min(clientWidth / plotWidth, clientHeight / plotHeight);

                                double scaledPlotWidth = plotWidth * scale2;
                                double scaledPlotHeight = plotHeight * scale2;

                                double topMargin = (height - scaledPlotHeight) * 0.5 / unitMultiplier;
                                double leftMargin = (width - scaledPlotWidth) * 0.5 / unitMultiplier;

                                leftNud.Value = leftMargin;
                                rightNud.Value = leftMargin;
                                topNud.Value = topMargin;
                                bottomNud.Value = topMargin;
                            }
                        }
                    }
                    else
                    {
                        MainWindow parent = this.FindAncestorOfType<MainWindow>();

                        if (parent != null)
                        {
                            double plotWidth = parent.PlotBottomRight.X - parent.PlotOrigin.X + 20;
                            double plotHeight = parent.PlotBottomRight.Y - parent.PlotOrigin.Y + 20;

                            if (cropRegionBox.SelectedIndex > 0)
                            {
                                plotWidth = cropRegionRects[cropRegionBox.SelectedIndex - 1].Size.Width;
                                plotHeight = cropRegionRects[cropRegionBox.SelectedIndex - 1].Size.Height;
                            }

                            if (plotWidth > 0 && plotHeight > 0)
                            {
                                double clientWidth = 14400 * 1;
                                double clientHeight = 14400 * 1;

                                double scale2 = Math.Min(1, Math.Min(clientWidth / plotWidth, clientHeight / plotHeight));



                                double scaledPlotWidth = plotWidth * scale2;
                                double scaledPlotHeight = plotHeight * scale2;

                                double topMargin = 0;
                                double leftMargin = 0;

                                widthNud.Value = (scaledPlotWidth + 2 * leftMargin) / unitMultiplier;
                                heightNud.Value = (scaledPlotHeight + 2 * topMargin) / unitMultiplier;

                                leftNud.Value = leftMargin / unitMultiplier;
                                rightNud.Value = leftMargin / unitMultiplier;
                                topNud.Value = topMargin / unitMultiplier;
                                bottomNud.Value = topMargin / unitMultiplier;
                            }
                        }
                    }

                    suppressChange = false;
                    drawPreview();
                }
            };

            units.PropertyChanged += (s, e) =>
            {
                if (e.Property == ComboBox.SelectedIndexProperty && !suppressChange)
                {
                    suppressChange = true;
                    int prevValue = (int)e.OldValue;
                    int newValue = (int)e.NewValue;

                    double unitMultiplier = prevValue == 0 ? 72 / 2.54 : prevValue == 1 ? 72 / 25.4 : 72;

                    double width = widthNud.Value * unitMultiplier;
                    double height = heightNud.Value * unitMultiplier;

                    double left = leftNud.Value * unitMultiplier;
                    double top = topNud.Value * unitMultiplier;
                    double right = rightNud.Value * unitMultiplier;
                    double bottom = bottomNud.Value * unitMultiplier;

                    unitMultiplier = newValue == 0 ? 72 / 2.54 : newValue == 1 ? 72 / 25.4 : 72;

                    widthNud.Maximum = 14400 / unitMultiplier;
                    heightNud.Maximum = 14400 / unitMultiplier;

                    widthNud.Value = width / unitMultiplier;
                    heightNud.Value = height / unitMultiplier;
                    leftNud.Value = left / unitMultiplier;
                    topNud.Value = top / unitMultiplier;
                    rightNud.Value = right / unitMultiplier;
                    bottomNud.Value = bottom / unitMultiplier;

                    string unitString;

                    if (newValue == 0)
                    {
                        unitString = "cm";
                    }
                    else if (newValue == 1)
                    {
                        unitString = "mm";
                    }
                    else
                    {
                        unitString = "in";
                    }

                    widthUnit.Text = unitString;
                    heightUnit.Text = unitString;
                    leftUnit.Text = unitString;
                    topUnit.Text = unitString;
                    rightUnit.Text = unitString;
                    bottomUnit.Text = unitString;
                    suppressChange = false;
                }
            };

            horizontalRadio.PropertyChanged += (s, e) =>
            {
                if (e.Property == RadioButton.IsCheckedProperty && !suppressChange)
                {
                    suppressChange = true;
                    double temp = widthNud.Value;
                    widthNud.Value = heightNud.Value;
                    heightNud.Value = temp;

                    double unitMultiplier = units.SelectedIndex == 0 ? 72 / 2.54 : units.SelectedIndex == 1 ? 72 / 25.4 : 72;

                    double width = widthNud.Value * unitMultiplier;
                    double height = heightNud.Value * unitMultiplier;

                    MainWindow parent = this.FindAncestorOfType<MainWindow>();

                    if (parent != null)
                    {
                        double plotWidth = parent.PlotBottomRight.X - parent.PlotOrigin.X + 20;
                        double plotHeight = parent.PlotBottomRight.Y - parent.PlotOrigin.Y + 20;

                        if (cropRegionBox.SelectedIndex > 0)
                        {
                            plotWidth = cropRegionRects[cropRegionBox.SelectedIndex - 1].Size.Width;
                            plotHeight = cropRegionRects[cropRegionBox.SelectedIndex - 1].Size.Height;
                        }

                        if (plotWidth > 0 && plotHeight > 0)
                        {
                            double clientWidth = width * 0.9;
                            double clientHeight = height * 0.9;

                            double scale2 = Math.Min(clientWidth / plotWidth, clientHeight / plotHeight);

                            double scaledPlotWidth = plotWidth * scale2;
                            double scaledPlotHeight = plotHeight * scale2;

                            double topMargin = (height - scaledPlotHeight) * 0.5 / unitMultiplier;
                            double leftMargin = (width - scaledPlotWidth) * 0.5 / unitMultiplier;

                            leftNud.Value = leftMargin;
                            rightNud.Value = leftMargin;
                            topNud.Value = topMargin;
                            bottomNud.Value = topMargin;
                        }
                    }

                    suppressChange = false;
                    drawPreview();
                }
            };

            void drawPreview()
            {
                double unitMultiplier = units.SelectedIndex == 0 ? 72 / 2.54 : units.SelectedIndex == 1 ? 72 / 25.4 : 72;

                double width = widthNud.Value * unitMultiplier;
                double height = heightNud.Value * unitMultiplier;

                double scale = 140.0 / Math.Max(width, height);

                double pageWidth = width * scale;
                double pageHeight = height * scale;

                Border page = new Border() { Background = new SolidColorBrush(this.FindAncestorOfType<MainWindow>().StateData.GraphBackgroundColour.ToAvalonia()), BorderThickness = new Thickness(1), BorderBrush = Brushes.Black, Width = pageWidth, Height = pageHeight, HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center, VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center };

                previewControl.Content = page;

                MainWindow parent = this.FindAncestorOfType<MainWindow>();

                if (parent != null)
                {
                    double plotWidth = parent.PlotBottomRight.X - parent.PlotOrigin.X + 20;
                    double plotHeight = parent.PlotBottomRight.Y - parent.PlotOrigin.Y + 20;

                    if (cropRegionBox.SelectedIndex > 0)
                    {
                        plotWidth = cropRegionRects[cropRegionBox.SelectedIndex - 1].Size.Width;
                        plotHeight = cropRegionRects[cropRegionBox.SelectedIndex - 1].Size.Height;
                    }

                    if (plotWidth > 0 && plotHeight > 0)
                    {
                        double clientWidth = pageWidth - leftNud.Value * unitMultiplier * scale - rightNud.Value * unitMultiplier * scale;
                        double clientHeight = pageHeight - topNud.Value * unitMultiplier * scale - bottomNud.Value * unitMultiplier * scale;

                        double scale2 = Math.Min(clientWidth / plotWidth, clientHeight / plotHeight);

                        double scaledPlotWidth = plotWidth * scale2;
                        double scaledPlotHeight = plotHeight * scale2;

                        Border plot = new Border() { BorderThickness = new Thickness(1), Background = new SolidColorBrush(Color.FromArgb(128, 0, 114, 178)), BorderBrush = Brushes.Black, Width = scaledPlotWidth, Height = scaledPlotHeight, HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Left, VerticalAlignment = Avalonia.Layout.VerticalAlignment.Top };
                        plot.RenderTransform = new TranslateTransform(leftNud.Value * unitMultiplier * scale - 1, topNud.Value * unitMultiplier * scale - 1);
                        page.Child = plot;
                    }
                }
            }

            void sizeChanged(object sender, AvaloniaPropertyChangedEventArgs e)
            {
                if (e.Property == NumericUpDown.ValueProperty && !suppressChange)
                {
                    suppressChange = true;

                    double unitMultiplier = units.SelectedIndex == 0 ? 72 / 2.54 : units.SelectedIndex == 1 ? 72 / 25.4 : 72;

                    double width = widthNud.Value * unitMultiplier;
                    double height = heightNud.Value * unitMultiplier;

                    if (width > height)
                    {
                        horizontalRadio.IsChecked = true;
                        verticalRadio.IsChecked = false;
                    }
                    else if (height > width)
                    {
                        horizontalRadio.IsChecked = false;
                        verticalRadio.IsChecked = true;
                    }

                    int index = 0;

                    for (int i = 0; i < PageSizes.Count; i++)
                    {
                        if ((Math.Abs((width - PageSizes[i].Item3) / PageSizes[i].Item3) <= 1e-4 && Math.Abs((height - PageSizes[i].Item4) / PageSizes[i].Item4) <= 1e-4) || (Math.Abs((width - PageSizes[i].Item4) / PageSizes[i].Item4) <= 1e-4 && Math.Abs((height - PageSizes[i].Item3) / PageSizes[i].Item3) <= 1e-4))
                        {
                            index = i + 1;
                            break;
                        }
                    }

                    pageSizeBox.SelectedIndex = index;

                    suppressChange = false;

                    drawPreview();
                }
            }

            widthNud.PropertyChanged += sizeChanged;
            heightNud.PropertyChanged += sizeChanged;

            leftNud.ValueChanged += (s, e) => { if (!suppressChange) { drawPreview(); } };
            rightNud.ValueChanged += (s, e) => { if (!suppressChange) { drawPreview(); } };
            topNud.ValueChanged += (s, e) => { if (!suppressChange) { drawPreview(); } };
            bottomNud.ValueChanged += (s, e) => { if (!suppressChange) { drawPreview(); } };

            saveButton.Click += async (s, e) =>
            {
                SaveFileDialog dialog = new SaveFileDialog() { Filters = new List<FileDialogFilter>() { new FileDialogFilter() { Name = "PDF document", Extensions = new List<string>() { "pdf" } } }, Title = "Save tree plot" };

                MainWindow parent = this.FindAncestorOfType<MainWindow>();

                string result = await dialog.ShowAsync(parent);

                if (!string.IsNullOrEmpty(result))
                {
                    try
                    {
                        if (System.IO.File.Exists(result))
                        {
                            System.IO.File.Delete(result);
                        }

                        if (VectSharp.FontFamily.DefaultFontLibrary is SimpleFontLibrary library)
                        {
                            foreach (KeyValuePair<string, Attachment> att in parent.StateData.Attachments)
                            {
                                AttachmentFontFamily fntF = att.Value.GetFontFamily();

                                if (fntF != null)
                                {
                                    library.Add(fntF);
                                }
                            }
                        }

                        VectSharp.Page pag = parent.RenderPlotToPage();

                        if (cropRegionBox.SelectedIndex > 0)
                        {
                            pag = ApplyCrop(pag, cropRegionRects[cropRegionBox.SelectedIndex - 1], parent.PlotOrigin);
                        }

                        double unitMultiplier = units.SelectedIndex == 0 ? 72 / 2.54 : units.SelectedIndex == 1 ? 72 / 25.4 : 72;

                        double width = widthNud.Value * unitMultiplier;
                        double height = heightNud.Value * unitMultiplier;

                        double plotWidth = pag.Width;
                        double plotHeight = pag.Height;

                        if (plotWidth > 0 && plotHeight > 0)
                        {

                            double clientWidth = width - leftNud.Value * unitMultiplier - rightNud.Value * unitMultiplier;
                            double clientHeight = height - topNud.Value * unitMultiplier - bottomNud.Value * unitMultiplier;

                            double scale2 = Math.Min(clientWidth / plotWidth, clientHeight / plotHeight);

                            VectSharp.Page newPag = new VectSharp.Page(width, height);

                            newPag.Graphics.Translate(leftNud.Value * unitMultiplier, topNud.Value * unitMultiplier);

                            newPag.Graphics.Scale(scale2, scale2);
                            newPag.Graphics.DrawGraphics(0, 0, pag.Graphics);

                            pag = newPag;

                            VectSharp.Document doc = new VectSharp.Document() { Pages = new List<VectSharp.Page>() { pag } };
                            doc.SaveAsPDF(result);
                        }

                        this.FindAncestorOfType<RibbonFilePage>().Close();
                    }
                    catch (Exception ex)
                    {
                        await new MessageBox("Error!", "Error while saving the PDF document:\n" + ex.Message).ShowDialog2(parent);
                    }
                }
            };

            void updatePreviewSize()
            {
                MainWindow parent = this.FindAncestorOfType<MainWindow>();

                if (parent != null)
                {

                    double unitMultiplier = units.SelectedIndex == 0 ? 72 / 2.54 : units.SelectedIndex == 1 ? 72 / 25.4 : 72;

                    if (parent != null)
                    {
                        double plotWidth = parent.PlotBottomRight.X - parent.PlotOrigin.X + 20;
                        double plotHeight = parent.PlotBottomRight.Y - parent.PlotOrigin.Y + 20;

                        if (cropRegionBox.SelectedIndex > 0)
                        {
                            plotWidth = cropRegionRects[cropRegionBox.SelectedIndex - 1].Size.Width;
                            plotHeight = cropRegionRects[cropRegionBox.SelectedIndex - 1].Size.Height;
                        }

                        if (plotWidth > 0 && plotHeight > 0)
                        {
                            double clientWidth = 14400 * 1;
                            double clientHeight = 14400 * 1;

                            double scale2 = Math.Min(1, Math.Min(clientWidth / plotWidth, clientHeight / plotHeight));

                            double scaledPlotWidth = plotWidth * scale2;
                            double scaledPlotHeight = plotHeight * scale2;

                            double topMargin = 0;
                            double leftMargin = 0;

                            widthNud.Value = (scaledPlotWidth + 2 * leftMargin) / unitMultiplier;
                            heightNud.Value = (scaledPlotHeight + 2 * topMargin) / unitMultiplier;

                            leftNud.Value = leftMargin / unitMultiplier;
                            rightNud.Value = leftMargin / unitMultiplier;
                            topNud.Value = topMargin / unitMultiplier;
                            bottomNud.Value = topMargin / unitMultiplier;
                        }
                    }

                    drawPreview();
                }
            }

            this.PropertyChanged += (s, e) =>
            {
                if (e.Property == ExportPage.IsVisibleProperty && (bool)e.NewValue)
                {
                    updatePreviewSize();
                }
            };

            cropRegionBox.PropertyChanged += (s, e) =>
            {
                if (e.Property == ComboBox.SelectedIndexProperty)
                {
                    updatePreviewSize();
                }
            };

            return mainContainer;
        }

        private Control GetSVGPage()
        {
            Grid mainContainer = new Grid() { Margin = new Avalonia.Thickness(25, 0, 0, 0) };
            mainContainer.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            mainContainer.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            mainContainer.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            mainContainer.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            mainContainer.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            mainContainer.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));

            mainContainer.Children.Add(new TextBlock() { FontSize = 20, Foreground = new SolidColorBrush(Color.FromRgb(0, 114, 178)), Text = "Export SVG" });

            StackPanel descriptionPanel = new StackPanel();


            descriptionPanel.Children.Add(new TextBlock()
            {
                FontSize = 12,
                Foreground = new SolidColorBrush(Color.FromRgb(102, 102, 102)),
                TextWrapping = TextWrapping.Wrap,
                Text = "The SVG format is an open standard released by the World Wide Web Consortium in 2001. SVG images can be displayed by all major web browsers (and thus, embedded in web pages) and can be edited by many vector graphics editors.",
                Margin = new Avalonia.Thickness(0, 0, 0, 5)
            });


            Grid.SetRow(descriptionPanel, 1);
            mainContainer.Children.Add(descriptionPanel);

            Grid regionRow = new Grid();
            regionRow.ColumnDefinitions.Add(new ColumnDefinition(0, GridUnitType.Auto) { MinWidth = 150, MaxWidth = 200 });
            regionRow.ColumnDefinitions.Add(new ColumnDefinition(1, GridUnitType.Star));

            ComboBox cropRegionBox;
            List<Rectangle> cropRegionRects;

            {
                TextBlock blk = new TextBlock() { Text = "Crop region:", Margin = new Thickness(0, 0, 10, 10), VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, FontSize = 14 };

                List<string> items = new List<string>() { "Entire plot" };

                cropRegionRects = new List<Rectangle>();

                cropRegionBox = new ComboBox() { Items = items, SelectedIndex = 2, Margin = new Thickness(0, 0, 0, 10), VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, MinWidth = 150, FontSize = 14 };

                Grid.SetColumn(cropRegionBox, 1);
                regionRow.Children.Add(blk);
                regionRow.Children.Add(cropRegionBox);

                Grid.SetRow(regionRow, 2);
                mainContainer.Children.Add(regionRow);
            }

            regionRow.IsVisible = cropRegionRects.Count > 0;

            this.PropertyChanged += (s, e) =>
            {
                if (e.Property == ExportPage.IsVisibleProperty)
                {
                    List<string> items = new List<string>() { "Entire plot" };

                    cropRegionRects = new List<Rectangle>();

                    InstanceStateData stateData = this.FindAncestorOfType<MainWindow>()?.StateData;

                    if (stateData != null && stateData.Tags.TryGetValue("5a8eb0c8-7139-4583-9e9e-375749a98973", out object cropRegionsObject) && cropRegionsObject != null && cropRegionsObject is Dictionary<string, (string, Rectangle)> cropRegions)
                    {
                        foreach (KeyValuePair<string, (string, Rectangle)> kvp in cropRegions)
                        {
                            if (IsAlive(kvp.Key, this.FindAncestorOfType<MainWindow>()))
                            {
                                items.Add(kvp.Value.Item1);
                                cropRegionRects.Add(kvp.Value.Item2);
                            }
                        }
                    }

                    cropRegionBox.Items = items;
                    cropRegionBox.SelectedIndex = 0;
                    regionRow.IsVisible = cropRegionRects.Count > 0;
                }
            };

            Grid textOptionRow = new Grid();
            textOptionRow.ColumnDefinitions.Add(new ColumnDefinition(0, GridUnitType.Auto) { MinWidth = 120, MaxWidth = 200 });
            textOptionRow.ColumnDefinitions.Add(new ColumnDefinition(1, GridUnitType.Star));

            ComboBox textOptionBox;

            {
                TextBlock blk = new TextBlock() { Text = "Text rendering:", Margin = new Thickness(0, 0, 10, 10), VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, FontSize = 14 };

                List<string> items = new List<string>() { "Embed full font", "Embed subsetted font", "Convert into paths", "Convert into paths using glyphs" };

                textOptionBox = new ComboBox() { Items = items, SelectedIndex = 3, Margin = new Thickness(0, 0, 0, 10), VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, MinWidth = 150, FontSize = 14 };

                Grid.SetColumn(textOptionBox, 1);
                textOptionRow.Children.Add(blk);
                textOptionRow.Children.Add(textOptionBox);

                Grid.SetRow(textOptionRow, 3);
                mainContainer.Children.Add(textOptionRow);
            }

            TextBlock note = new TextBlock()
            {
                FontSize = 12,
                Foreground = new SolidColorBrush(Color.FromRgb(102, 102, 102)),
                TextWrapping = TextWrapping.Wrap,
                Text = "Note: converting all text into paths means that it will always look right, but it will not be selectable, searchable or editable.",
                Margin = new Avalonia.Thickness(0, 0, 0, 5)
            };
            Grid.SetRow(note, 4);
            mainContainer.Children.Add(note);

            StackPanel saveButtonContent = new StackPanel() { Orientation = Orientation.Horizontal, VerticalAlignment = VerticalAlignment.Center };
            saveButtonContent.Children.Add(new DPIAwareBox(GetSaveIcon) { Width = 16, Height = 16, VerticalAlignment = VerticalAlignment.Center });
            saveButtonContent.Children.Add(new TextBlock() { Text = "Export SVG", Margin = new Thickness(5, 0, 0, 0), VerticalAlignment = VerticalAlignment.Center, Foreground = Brushes.Black });

            Button saveButton = new Button() { Content = saveButtonContent, HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center, Margin = new Avalonia.Thickness(0, 5, 0, 0) };
            saveButton.Classes.Add("SideBarButton");
            Grid.SetRow(saveButton, 5);
            mainContainer.Children.Add(saveButton);

            textOptionBox.SelectionChanged += (s, e) =>
            {
                if (textOptionBox.SelectedIndex == 0)
                {
                    note.Text = "Note: selecting this option means that text will be selectable, searchable and editable. However, some editors do not support embedded font files and may display the text using a fallback font instead. In any case, they should preserve the embedded font so that when the file is viewed e.g. with a web browser it looks right.";
                }
                else if (textOptionBox.SelectedIndex == 1)
                {
                    note.Text = "Note: selecting this option means that text will be selectable and searchable. Editing may not be possible due to missing glyphs. Furthermore, some editors do not support embedded font files and may display the text using a fallback font instead. In any case, they should preserve the embedded font so that when the file is viewed e.g. with a web browser it looks right.";
                }
                else if (textOptionBox.SelectedIndex == 2)
                {
                    note.Text = "Note: converting all text into paths means that it will always look right, but it will not be selectable, searchable or editable. The output file will be rather large (use the \"Convert into paths using glyphs\" option to address this).";
                }
                else if (textOptionBox.SelectedIndex == 3)
                {
                    note.Text = "Note: converting all text into paths means that it will always look right, but it will not be selectable, searchable or editable. Using glyphs means that the output file will not be too large, but some editors do not support embedded glyphs.";
                }
            };

            this.PropertyChanged += (s, e) =>
            {
                if (e.Property == ExportPage.IsVisibleProperty && (bool)e.NewValue)
                {
                    textOptionBox.SelectedIndex = 3;
                }
            };

            saveButton.Click += async (s, e) =>
            {
                SaveFileDialog dialog = new SaveFileDialog() { Filters = new List<FileDialogFilter>() { new FileDialogFilter() { Name = "SVG file", Extensions = new List<string>() { "svg" } } }, Title = "Save tree plot" };

                MainWindow parent = this.FindAncestorOfType<MainWindow>();

                string result = await dialog.ShowAsync(parent);

                if (!string.IsNullOrEmpty(result))
                {
                    try
                    {
                        if (System.IO.File.Exists(result))
                        {
                            System.IO.File.Delete(result);
                        }

                        if (VectSharp.FontFamily.DefaultFontLibrary is SimpleFontLibrary library)
                        {
                            foreach (KeyValuePair<string, Attachment> att in parent.StateData.Attachments)
                            {
                                AttachmentFontFamily fntF = att.Value.GetFontFamily();

                                if (fntF != null)
                                {
                                    library.Add(fntF);
                                }
                            }
                        }

                        VectSharp.Page pag = parent.RenderPlotToPage();

                        if (cropRegionBox.SelectedIndex > 0)
                        {
                            pag = ApplyCrop(pag, cropRegionRects[cropRegionBox.SelectedIndex - 1], parent.PlotOrigin);
                        }

                        PreflightCheckRasterImages(pag);

                        pag.SaveAsSVG(result, textOptionBox.SelectedIndex == 0 ? SVGContextInterpreter.TextOptions.EmbedFonts : textOptionBox.SelectedIndex == 1 ? SVGContextInterpreter.TextOptions.SubsetFonts : textOptionBox.SelectedIndex == 2 ? SVGContextInterpreter.TextOptions.ConvertIntoPaths : SVGContextInterpreter.TextOptions.ConvertIntoPathsUsingGlyphs);

                        this.FindAncestorOfType<RibbonFilePage>().Close();
                    }
                    catch (Exception ex)
                    {
                        await new MessageBox("Error!", "Error while saving the SVG file:\n" + ex.Message).ShowDialog2(parent);
                    }
                }
            };

            return mainContainer;
        }

        private static void PreflightCheckRasterImages(Page pag)
        {
            IEnumerable actions = (IEnumerable)typeof(Graphics).GetField("Actions", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(pag.Graphics);

            foreach (object action in actions)
            {
                if (action.GetType().FullName == "VectSharp.RasterImageAction")
                {
                    RasterImage image = (RasterImage)action.GetType().GetProperty("Image", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic).GetValue(action);

                    try
                    {
                        if (!image.PNGStream.CanRead)
                        {
                            image.ClearPNGCache();
                        }
                    }
                    catch (ObjectDisposedException)
                    {
                        image.ClearPNGCache();
                    }
                }
            }
        }

        private static Page ApplyCrop(Page pag, Rectangle cropRegion, VectSharp.Point origin)
        {
            VectSharp.Point location = cropRegion.Location;
            VectSharp.Size size = cropRegion.Size;

            pag.Crop(new VectSharp.Point(-origin.X + 10 + location.X, -origin.Y + 10 + location.Y), size, removeClippedGraphics: true);

            Page pag2 = new Page(size.Width, size.Height);

            pag2.Graphics.SetClippingPath(0, 0, size.Width, size.Height);

            pag2.Graphics.DrawGraphics(0, 0, pag.Graphics);

            pag2.Background = pag.Background;

            return pag2;
        }

        private Control GetPNGPage()
        {
            Grid mainContainer = new Grid() { Margin = new Avalonia.Thickness(25, 0, 0, 0) };
            mainContainer.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            mainContainer.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            mainContainer.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            mainContainer.RowDefinitions.Add(new RowDefinition(1, GridUnitType.Star) { MaxHeight = 170 });
            mainContainer.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));

            mainContainer.Children.Add(new TextBlock() { FontSize = 20, Foreground = new SolidColorBrush(Color.FromRgb(0, 114, 178)), Text = "Export PNG/TIFF" });

            StackPanel descriptionPanel = new StackPanel();


            descriptionPanel.Children.Add(new TextBlock()
            {
                FontSize = 12,
                Foreground = new SolidColorBrush(Color.FromRgb(102, 102, 102)),
                TextWrapping = TextWrapping.Wrap,
                Text = "The PNG and TIFF formats are raster formats released in 1996 and 1986, respectively. Being in a raster format, PNG/TIFF images look blurry when resized, and thus PDF or SVG files are more suited for publication-ready files.",
                Margin = new Avalonia.Thickness(0, 0, 0, 5)
            });

            descriptionPanel.Children.Add(new TextBlock()
            {
                FontSize = 12,
                Foreground = new SolidColorBrush(Color.FromRgb(102, 102, 102)),
                TextWrapping = TextWrapping.Wrap,
                Text = "The advantage of PNG/TIFF images is that even with large trees and many layers, the file size and rendering time of such images depend mainly on the chosen resolution, and not on the complexity of the plot.",
                Margin = new Avalonia.Thickness(0, 0, 0, 5)
            });


            descriptionPanel.Children.Add(new TextBlock()
            {
                FontSize = 12,
                Foreground = new SolidColorBrush(Color.FromRgb(102, 102, 102)),
                TextWrapping = TextWrapping.Wrap,
                Text = "Therefore, a PNG or TIFF image might be the best choice if you have a very large tree and so many plot elements that a PDF or SVG file become unwieldy.",
                Margin = new Avalonia.Thickness(0, 0, 0, 5)
            });

            Grid.SetRow(descriptionPanel, 1);
            mainContainer.Children.Add(descriptionPanel);

            Grid regionRow = new Grid();
            regionRow.ColumnDefinitions.Add(new ColumnDefinition(0, GridUnitType.Auto) { MinWidth = 150, MaxWidth = 200 });
            regionRow.ColumnDefinitions.Add(new ColumnDefinition(1, GridUnitType.Star));

            ComboBox cropRegionBox;
            List<Rectangle> cropRegionRects;

            {
                TextBlock blk = new TextBlock() { Text = "Crop region:", Margin = new Thickness(0, 0, 10, 10), VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, FontSize = 14 };

                List<string> items = new List<string>() { "Entire plot" };

                cropRegionRects = new List<Rectangle>();

                cropRegionBox = new ComboBox() { Items = items, SelectedIndex = 2, Margin = new Thickness(0, 0, 0, 10), VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, MinWidth = 150, FontSize = 14 };

                Grid.SetColumn(cropRegionBox, 1);
                regionRow.Children.Add(blk);
                regionRow.Children.Add(cropRegionBox);

                Grid.SetRow(regionRow, 2);
                mainContainer.Children.Add(regionRow);
            }

            regionRow.IsVisible = cropRegionRects.Count > 0;

            this.PropertyChanged += (s, e) =>
            {
                if (e.Property == ExportPage.IsVisibleProperty)
                {
                    List<string> items = new List<string>() { "Entire plot" };

                    cropRegionRects = new List<Rectangle>();

                    InstanceStateData stateData = this.FindAncestorOfType<MainWindow>()?.StateData;

                    if (stateData != null && stateData.Tags.TryGetValue("5a8eb0c8-7139-4583-9e9e-375749a98973", out object cropRegionsObject) && cropRegionsObject != null && cropRegionsObject is Dictionary<string, (string, Rectangle)> cropRegions)
                    {
                        foreach (KeyValuePair<string, (string, Rectangle)> kvp in cropRegions)
                        {
                            if (IsAlive(kvp.Key, this.FindAncestorOfType<MainWindow>()))
                            {
                                items.Add(kvp.Value.Item1);
                                cropRegionRects.Add(kvp.Value.Item2);
                            }
                        }
                    }

                    cropRegionBox.Items = items;
                    cropRegionBox.SelectedIndex = 0;
                    regionRow.IsVisible = cropRegionRects.Count > 0;
                }
            };

            ScrollViewer plotSizeContainer = new ScrollViewer() { HorizontalScrollBarVisibility = Avalonia.Controls.Primitives.ScrollBarVisibility.Disabled, VerticalScrollBarVisibility = Avalonia.Controls.Primitives.ScrollBarVisibility.Auto, AllowAutoHide = false, Padding = new Avalonia.Thickness(0, 0, 17, 0) };
            Grid.SetRow(plotSizeContainer, 3);
            mainContainer.Children.Add(plotSizeContainer);

            Grid plotSizeGrid = new Grid() { VerticalAlignment = Avalonia.Layout.VerticalAlignment.Top };
            plotSizeGrid.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            plotSizeGrid.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            plotSizeGrid.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            plotSizeGrid.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            plotSizeGrid.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            plotSizeGrid.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            plotSizeGrid.RowDefinitions.Add(new RowDefinition(0, GridUnitType.Auto));
            plotSizeGrid.ColumnDefinitions.Add(new ColumnDefinition(1, GridUnitType.Star));
            plotSizeGrid.ColumnDefinitions.Add(new ColumnDefinition(0, GridUnitType.Auto));
            plotSizeGrid.ColumnDefinitions.Add(new ColumnDefinition(1, GridUnitType.Star));
            plotSizeGrid.ColumnDefinitions.Add(new ColumnDefinition(0, GridUnitType.Auto));
            plotSizeContainer.Content = plotSizeGrid;

            {
                TextBlock blk = new TextBlock()
                {
                    FontSize = 16,
                    FontWeight = FontWeight.Bold,
                    Text = "Plot size",
                    Margin = new Avalonia.Thickness(0, 0, 0, 5)
                };
                Grid.SetColumnSpan(blk, 4);
                plotSizeGrid.Children.Add(blk);
            }


            Grid plotSizeRow = new Grid();

            plotSizeRow.ColumnDefinitions.Add(new ColumnDefinition(1, GridUnitType.Star));
            plotSizeRow.ColumnDefinitions.Add(new ColumnDefinition(0, GridUnitType.Auto));
            plotSizeRow.ColumnDefinitions.Add(new ColumnDefinition(1, GridUnitType.Star));

            TextBlock plotSizePt = new TextBlock()
            {
                FontSize = 14,
                Foreground = new SolidColorBrush(Color.FromRgb(102, 102, 102)),
                Text = "10 x 10 pt",
                Margin = new Avalonia.Thickness(0, 0, 0, 5)
            };
            Grid.SetColumn(plotSizePt, 1);
            plotSizeRow.Children.Add(plotSizePt);

            Grid.SetRow(plotSizeRow, 1);
            Grid.SetColumnSpan(plotSizeRow, 4);
            plotSizeGrid.Children.Add(plotSizeRow);

            {
                TextBlock blk = new TextBlock()
                {
                    FontSize = 16,
                    FontWeight = FontWeight.Bold,
                    Text = "Image size"
                };
                Grid.SetRow(blk, 2);
                Grid.SetColumnSpan(blk, 4);
                plotSizeGrid.Children.Add(blk);
            }

            {
                TextBlock blk = new TextBlock()
                {
                    FontSize = 14,
                    Text = "Width",
                    Margin = new Avalonia.Thickness(0, 0, 0, 5),
                    VerticalAlignment = VerticalAlignment.Center,
                    HorizontalAlignment = HorizontalAlignment.Center
                };
                Grid.SetRow(blk, 3);
                plotSizeGrid.Children.Add(blk);
            }

            {
                TextBlock blk = new TextBlock()
                {
                    FontSize = 14,
                    Text = "Height",
                    Margin = new Avalonia.Thickness(0, 0, 0, 5),
                    VerticalAlignment = VerticalAlignment.Center,
                    HorizontalAlignment = HorizontalAlignment.Center
                };
                Grid.SetRow(blk, 3);
                Grid.SetColumn(blk, 2);
                plotSizeGrid.Children.Add(blk);
            }

            {
                TextBlock blk = new TextBlock()
                {
                    FontSize = 14,
                    Text = "px",
                    Margin = new Avalonia.Thickness(3, 0, 0, 5),
                    HorizontalAlignment = HorizontalAlignment.Left,
                    VerticalAlignment = VerticalAlignment.Center
                };
                Grid.SetColumn(blk, 1);
                Grid.SetRow(blk, 4);
                plotSizeGrid.Children.Add(blk);
            }

            {
                TextBlock blk = new TextBlock()
                {
                    FontSize = 14,
                    Text = "px",
                    Margin = new Avalonia.Thickness(3, 0, 0, 5),
                    HorizontalAlignment = HorizontalAlignment.Left,
                    VerticalAlignment = VerticalAlignment.Center
                };
                Grid.SetColumn(blk, 3);
                Grid.SetRow(blk, 4);
                plotSizeGrid.Children.Add(blk);
            }

            ComboBox units = new ComboBox() { VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, Padding = new Avalonia.Thickness(5, 2, 2, 2), FontSize = 13, Margin = new Avalonia.Thickness(5, 0, 0, 0), Width = 65 };
            units.Items = new List<string>() { "cm", "mm", "in" };
            units.SelectedIndex = 0;

            Grid.SetColumn(units, 1);
            Grid.SetRow(units, 5);
            plotSizeGrid.Children.Add(units);

            TextBlock unitsReplicate = new TextBlock()
            {
                FontSize = 14,
                Text = "cm",
                Margin = new Avalonia.Thickness(3, 0, 0, 5),
                HorizontalAlignment = HorizontalAlignment.Left,
                VerticalAlignment = VerticalAlignment.Center,
                Width = 27
            };

            Grid.SetColumn(unitsReplicate, 3);
            Grid.SetRow(unitsReplicate, 5);
            plotSizeGrid.Children.Add(unitsReplicate);

            NumericUpDown widthPxNud = new NumericUpDown() { Margin = new Avalonia.Thickness(5, 0, 0, 0), Padding = new Avalonia.Thickness(2, 0, 2, 0), VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, Minimum = 0, FormatString = "{0:0}", FontSize = 14, Increment = 1 };
            NumericUpDown heightPxNud = new NumericUpDown() { Margin = new Avalonia.Thickness(5, 0, 0, 0), Padding = new Avalonia.Thickness(2, 0, 2, 0), VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, Minimum = 0, FormatString = "{0:0}", FontSize = 14, Increment = 1 };

            Grid.SetRow(widthPxNud, 4);
            Grid.SetColumn(widthPxNud, 0);
            plotSizeGrid.Children.Add(widthPxNud);

            Grid.SetRow(heightPxNud, 4);
            Grid.SetColumn(heightPxNud, 2);
            plotSizeGrid.Children.Add(heightPxNud);

            NumericUpDown widthCmNud = new NumericUpDown() { Margin = new Avalonia.Thickness(5, 0, 0, 0), Padding = new Avalonia.Thickness(2, 0, 2, 0), VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, Minimum = 0, FormatString = "{0:0.##}", FontSize = 14, Increment = 0.1 };
            NumericUpDown heightCmNud = new NumericUpDown() { Margin = new Avalonia.Thickness(5, 0, 0, 0), Padding = new Avalonia.Thickness(2, 0, 2, 0), VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, Minimum = 0, FormatString = "{0:0.##}", FontSize = 14, Increment = 0.1 };

            Grid.SetRow(widthCmNud, 5);
            Grid.SetColumn(widthCmNud, 0);
            plotSizeGrid.Children.Add(widthCmNud);

            Grid.SetRow(heightCmNud, 5);
            Grid.SetColumn(heightCmNud, 2);
            plotSizeGrid.Children.Add(heightCmNud);

            Grid resolutionGrid = new Grid() { HorizontalAlignment = HorizontalAlignment.Center };
            resolutionGrid.ColumnDefinitions.Add(new ColumnDefinition(0, GridUnitType.Auto));
            resolutionGrid.ColumnDefinitions.Add(new ColumnDefinition(0, GridUnitType.Auto));
            resolutionGrid.ColumnDefinitions.Add(new ColumnDefinition(0, GridUnitType.Auto));

            {
                TextBlock blk = new TextBlock()
                {
                    FontSize = 14,
                    Text = "Resolution:",
                    Margin = new Avalonia.Thickness(0, 0, 0, 5),
                    VerticalAlignment = VerticalAlignment.Center
                };

                resolutionGrid.Children.Add(blk);
            }

            {
                TextBlock blk = new TextBlock()
                {
                    FontSize = 14,
                    Text = "dpi",
                    Margin = new Avalonia.Thickness(3, 0, 0, 5),
                    VerticalAlignment = VerticalAlignment.Center,
                    HorizontalAlignment = HorizontalAlignment.Left
                };

                Grid.SetColumn(blk, 2);
                resolutionGrid.Children.Add(blk);
            }

            NumericUpDown resolutionNud = new NumericUpDown() { Margin = new Avalonia.Thickness(5, 0, 0, 5), Padding = new Avalonia.Thickness(2, 0, 2, 0), VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, Minimum = 0, FormatString = "{0:0.##}", FontSize = 14, Increment = 1, Width = 100 };

            Grid.SetColumn(resolutionNud, 1);
            resolutionGrid.Children.Add(resolutionNud);

            Grid.SetRow(resolutionGrid, 6);
            Grid.SetColumnSpan(resolutionGrid, 4);
            plotSizeGrid.Children.Add(resolutionGrid);

            Grid saveButtons = new Grid();
            saveButtons.ColumnDefinitions.Add(new ColumnDefinition(1, GridUnitType.Star));
            saveButtons.ColumnDefinitions.Add(new ColumnDefinition(0, GridUnitType.Auto));
            saveButtons.ColumnDefinitions.Add(new ColumnDefinition(1, GridUnitType.Star));
            saveButtons.ColumnDefinitions.Add(new ColumnDefinition(0, GridUnitType.Auto));
            saveButtons.ColumnDefinitions.Add(new ColumnDefinition(1, GridUnitType.Star));
            Grid.SetRow(saveButtons, 4);
            mainContainer.Children.Add(saveButtons);

            Button savePNGButton = new Button() { HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center, Margin = new Avalonia.Thickness(0, 5, 0, 0) };
            savePNGButton.Classes.Add("SideBarButton");
            Grid.SetColumn(savePNGButton, 1);
            saveButtons.Children.Add(savePNGButton);

            {
                StackPanel saveButtonContent = new StackPanel() { Orientation = Orientation.Horizontal, VerticalAlignment = VerticalAlignment.Center };
                saveButtonContent.Children.Add(new DPIAwareBox(GetSaveIcon) { Width = 16, Height = 16, VerticalAlignment = VerticalAlignment.Center });
                saveButtonContent.Children.Add(new TextBlock() { Text = "Export PNG", Margin = new Thickness(5, 0, 0, 0), VerticalAlignment = VerticalAlignment.Center, Foreground = Brushes.Black });
                savePNGButton.Content = saveButtonContent;
            }

            Button saveTIFFButton = new Button() { HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center, Margin = new Avalonia.Thickness(0, 5, 0, 0) };
            saveTIFFButton.Classes.Add("SideBarButton");
            Grid.SetColumn(saveTIFFButton, 3);
            saveButtons.Children.Add(saveTIFFButton);

            {
                StackPanel saveButtonContent = new StackPanel() { Orientation = Orientation.Horizontal, VerticalAlignment = VerticalAlignment.Center };
                saveButtonContent.Children.Add(new DPIAwareBox(GetSaveIcon) { Width = 16, Height = 16, VerticalAlignment = VerticalAlignment.Center });
                saveButtonContent.Children.Add(new TextBlock() { Text = "Export TIFF", Margin = new Thickness(5, 0, 0, 0), VerticalAlignment = VerticalAlignment.Center, Foreground = Brushes.Black });
                saveTIFFButton.Content = saveButtonContent;
            }


            bool suppressChange = false;

            widthPxNud.ValueChanged += (s, e) =>
            {
                if (!suppressChange)
                {
                    MainWindow parent = this.FindAncestorOfType<MainWindow>();

                    if (parent != null)
                    {
                        double plotWidth = parent.PlotBottomRight.X - parent.PlotOrigin.X + 20;
                        double plotHeight = parent.PlotBottomRight.Y - parent.PlotOrigin.Y + 20;

                        if (cropRegionBox.SelectedIndex > 0)
                        {
                            plotWidth = cropRegionRects[cropRegionBox.SelectedIndex - 1].Size.Width;
                            plotHeight = cropRegionRects[cropRegionBox.SelectedIndex - 1].Size.Height;
                        }

                        double scale = widthPxNud.Value / plotWidth;

                        suppressChange = true;

                        heightPxNud.Value = plotHeight * scale;

                        double resolution = widthPxNud.Value / widthCmNud.Value;

                        if (units.SelectedIndex == 0)
                        {
                            resolution *= 2.54;
                        }
                        else if (units.SelectedIndex == 1)
                        {
                            resolution *= 25.4;
                        }

                        resolutionNud.Value = resolution;

                        suppressChange = false;
                    }
                }
            };

            heightPxNud.ValueChanged += (s, e) =>
            {
                if (!suppressChange)
                {
                    MainWindow parent = this.FindAncestorOfType<MainWindow>();

                    if (parent != null)
                    {
                        double plotWidth = parent.PlotBottomRight.X - parent.PlotOrigin.X + 20;
                        double plotHeight = parent.PlotBottomRight.Y - parent.PlotOrigin.Y + 20;

                        if (cropRegionBox.SelectedIndex > 0)
                        {
                            plotWidth = cropRegionRects[cropRegionBox.SelectedIndex - 1].Size.Width;
                            plotHeight = cropRegionRects[cropRegionBox.SelectedIndex - 1].Size.Height;
                        }

                        double scale = heightPxNud.Value / plotHeight;

                        suppressChange = true;

                        widthPxNud.Value = plotWidth * scale;

                        double resolution = heightPxNud.Value / heightCmNud.Value;

                        if (units.SelectedIndex == 0)
                        {
                            resolution *= 2.54;
                        }
                        else if (units.SelectedIndex == 1)
                        {
                            resolution *= 25.4;
                        }

                        resolutionNud.Value = resolution;

                        suppressChange = false;
                    }
                }
            };

            resolutionNud.ValueChanged += (s, e) =>
            {
                if (!suppressChange)
                {
                    MainWindow parent = this.FindAncestorOfType<MainWindow>();

                    if (parent != null)
                    {
                        double plotWidth = parent.PlotBottomRight.X - parent.PlotOrigin.X + 20;
                        double plotHeight = parent.PlotBottomRight.Y - parent.PlotOrigin.Y + 20;

                        if (cropRegionBox.SelectedIndex > 0)
                        {
                            plotWidth = cropRegionRects[cropRegionBox.SelectedIndex - 1].Size.Width;
                            plotHeight = cropRegionRects[cropRegionBox.SelectedIndex - 1].Size.Height;
                        }

                        double resolution = resolutionNud.Value;

                        if (units.SelectedIndex == 0)
                        {
                            resolution /= 2.54;
                        }
                        else if (units.SelectedIndex == 1)
                        {
                            resolution /= 25.4;
                        }

                        double scale = widthCmNud.Value * resolution / plotWidth;

                        suppressChange = true;

                        widthPxNud.Value = Math.Round(plotWidth * scale);
                        heightPxNud.Value = Math.Round(plotHeight * scale);

                        suppressChange = false;
                    }
                }
            };

            widthCmNud.ValueChanged += (s, e) =>
            {
                if (!suppressChange)
                {
                    MainWindow parent = this.FindAncestorOfType<MainWindow>();

                    if (parent != null)
                    {
                        double plotWidth = parent.PlotBottomRight.X - parent.PlotOrigin.X + 20;
                        double plotHeight = parent.PlotBottomRight.Y - parent.PlotOrigin.Y + 20;

                        if (cropRegionBox.SelectedIndex > 0)
                        {
                            plotWidth = cropRegionRects[cropRegionBox.SelectedIndex - 1].Size.Width;
                            plotHeight = cropRegionRects[cropRegionBox.SelectedIndex - 1].Size.Height;
                        }

                        double resolution = resolutionNud.Value;

                        if (units.SelectedIndex == 0)
                        {
                            resolution /= 2.54;
                        }
                        else if (units.SelectedIndex == 1)
                        {
                            resolution /= 25.4;
                        }

                        double scale = widthCmNud.Value * resolution / plotWidth;

                        suppressChange = true;

                        widthPxNud.Value = plotWidth * scale;
                        heightPxNud.Value = plotHeight * scale;

                        heightCmNud.Value = widthCmNud.Value / plotWidth * plotHeight;

                        suppressChange = false;
                    }
                }
            };

            heightCmNud.ValueChanged += (s, e) =>
            {
                if (!suppressChange)
                {
                    MainWindow parent = this.FindAncestorOfType<MainWindow>();

                    if (parent != null)
                    {
                        double plotWidth = parent.PlotBottomRight.X - parent.PlotOrigin.X + 20;
                        double plotHeight = parent.PlotBottomRight.Y - parent.PlotOrigin.Y + 20;

                        if (cropRegionBox.SelectedIndex > 0)
                        {
                            plotWidth = cropRegionRects[cropRegionBox.SelectedIndex - 1].Size.Width;
                            plotHeight = cropRegionRects[cropRegionBox.SelectedIndex - 1].Size.Height;
                        }

                        double resolution = resolutionNud.Value;

                        if (units.SelectedIndex == 0)
                        {
                            resolution /= 2.54;
                        }
                        else if (units.SelectedIndex == 1)
                        {
                            resolution /= 25.4;
                        }

                        double scale = heightCmNud.Value * resolution / plotHeight;

                        suppressChange = true;

                        widthPxNud.Value = plotWidth * scale;
                        heightPxNud.Value = plotHeight * scale;

                        widthCmNud.Value = heightCmNud.Value / plotHeight * plotWidth;

                        suppressChange = false;
                    }
                }
            };

            this.PropertyChanged += (s, e) =>
            {
                if (e.Property == ExportPage.IsVisibleProperty && (bool)e.NewValue)
                {
                    MainWindow parent = this.FindAncestorOfType<MainWindow>();

                    if (parent != null)
                    {
                        double plotWidth = parent.PlotBottomRight.X - parent.PlotOrigin.X + 20;
                        double plotHeight = parent.PlotBottomRight.Y - parent.PlotOrigin.Y + 20;

                        if (cropRegionBox.SelectedIndex > 0)
                        {
                            plotWidth = cropRegionRects[cropRegionBox.SelectedIndex - 1].Size.Width;
                            plotHeight = cropRegionRects[cropRegionBox.SelectedIndex - 1].Size.Height;
                        }

                        plotSizePt.Text = plotWidth.ToString("0.##", System.Globalization.CultureInfo.InvariantCulture) + " x " + plotHeight.ToString("0.##", System.Globalization.CultureInfo.InvariantCulture) + " pt";

                        suppressChange = true;

                        widthPxNud.Value = Math.Round(plotWidth * 300 / 72);
                        heightPxNud.Value = Math.Round(plotHeight * 300 / 72);

                        widthCmNud.Value = Math.Round(plotWidth * 300 / 72) / 300 * 2.54;
                        heightCmNud.Value = Math.Round(plotHeight * 300 / 72) / 300 * 2.54;

                        resolutionNud.Value = 300;

                        suppressChange = false;
                    }
                }
            };

            units.SelectionChanged += (s, e) =>
            {
                switch (units.SelectedIndex)
                {
                    case 0:
                        unitsReplicate.Text = "cm";
                        break;

                    case 1:
                        unitsReplicate.Text = "mm";
                        break;

                    case 2:
                        unitsReplicate.Text = "in";
                        break;
                }

                if (!suppressChange)
                {
                    MainWindow parent = this.FindAncestorOfType<MainWindow>();

                    if (parent != null)
                    {
                        double resolution = resolutionNud.Value;

                        if (units.SelectedIndex == 0)
                        {
                            resolution /= 2.54;
                        }
                        else if (units.SelectedIndex == 1)
                        {
                            resolution /= 25.4;
                        }

                        suppressChange = true;

                        widthCmNud.Value = Math.Round(widthPxNud.Value / resolution);
                        heightCmNud.Value = Math.Round(heightPxNud.Value / resolution);

                        suppressChange = false;
                    }
                }
            };

            cropRegionBox.SelectionChanged += (s, e) =>
            {
                MainWindow parent = this.FindAncestorOfType<MainWindow>();

                if (parent != null)
                {
                    double plotWidth = parent.PlotBottomRight.X - parent.PlotOrigin.X + 20;
                    double plotHeight = parent.PlotBottomRight.Y - parent.PlotOrigin.Y + 20;

                    if (cropRegionBox.SelectedIndex > 0)
                    {
                        plotWidth = cropRegionRects[cropRegionBox.SelectedIndex - 1].Size.Width;
                        plotHeight = cropRegionRects[cropRegionBox.SelectedIndex - 1].Size.Height;
                    }

                    plotSizePt.Text = plotWidth.ToString("0.##", System.Globalization.CultureInfo.InvariantCulture) + " x " + plotHeight.ToString("0.##", System.Globalization.CultureInfo.InvariantCulture) + " pt";

                    suppressChange = true;

                    widthPxNud.Value = Math.Round(plotWidth * 300 / 72);
                    heightPxNud.Value = Math.Round(plotHeight * 300 / 72);

                    widthCmNud.Value = Math.Round(plotWidth * 300 / 72) / 300 * 2.54;
                    heightCmNud.Value = Math.Round(plotHeight * 300 / 72) / 300 * 2.54;

                    resolutionNud.Value = 300;

                    suppressChange = false;
                }
            };

            savePNGButton.Click += async (s, e) =>
            {
                SaveFileDialog dialog = new SaveFileDialog() { Filters = new List<FileDialogFilter>() { new FileDialogFilter() { Name = "PNG file", Extensions = new List<string>() { "png" } } }, Title = "Save tree plot" };

                MainWindow parent = this.FindAncestorOfType<MainWindow>();

                string result = await dialog.ShowAsync(parent);

                if (!string.IsNullOrEmpty(result))
                {
                    ProgressWindow win = new ProgressWindow() { ProgressText = "Exporting PNG image..." };

                    Task winTask = win.ShowDialog2(parent);

                    Exception exception = null;

                    await Task.Run(() =>
                    {
                        try
                        {
                            if (System.IO.File.Exists(result))
                            {
                                System.IO.File.Delete(result);
                            }

                            if (VectSharp.FontFamily.DefaultFontLibrary is SimpleFontLibrary library)
                            {
                                foreach (KeyValuePair<string, Attachment> att in parent.StateData.Attachments)
                                {
                                    AttachmentFontFamily fntF = att.Value.GetFontFamily();

                                    if (fntF != null)
                                    {
                                        library.Add(fntF);
                                    }
                                }
                            }

                            VectSharp.Page pag = parent.RenderPlotToPage();

                            if (cropRegionBox.SelectedIndex > 0)
                            {
                                pag = ApplyCrop(pag, cropRegionRects[cropRegionBox.SelectedIndex - 1], parent.PlotOrigin);
                            }

                            double scale = widthPxNud.Value / pag.Width;

                            SixLabors.ImageSharp.Image image;

                            using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
                            {
                                pag.SaveAsPNG(ms, scale);

                                ms.Seek(0, System.IO.SeekOrigin.Begin);

                                image = SixLabors.ImageSharp.Image<SixLabors.ImageSharp.PixelFormats.Rgba32>.Load(ms);
                            }

                            image.Metadata.ResolutionUnits = SixLabors.ImageSharp.Metadata.PixelResolutionUnit.PixelsPerInch;
                            image.Metadata.HorizontalResolution = resolutionNud.Value;
                            image.Metadata.VerticalResolution = resolutionNud.Value;

                            SixLabors.ImageSharp.ImageExtensions.SaveAsPng(image, result);

                        }
                        catch (Exception ex)
                        {
                            exception = ex;
                        }
                    });

                    win.Close();
                    await winTask;

                    if (exception != null)
                    {
                        await new MessageBox("Error!", "Error while saving the PNG file:\n" + exception.Message).ShowDialog2(parent);
                    }
                    else
                    {
                        this.FindAncestorOfType<RibbonFilePage>().Close();
                    }
                }
            };

            saveTIFFButton.Click += async (s, e) =>
            {
                MainWindow parent = this.FindAncestorOfType<MainWindow>();

                if (parent.GraphBackground.A < 1)
                {
                    MessageBox box = new MessageBox("Question", "The plot has a transparent background, but the TIFF encoder does not support images with a transparent background. Do you wish to proceed anyways?", MessageBox.MessageBoxButtonTypes.YesNo, MessageBox.MessageBoxIconTypes.QuestionMark);
                    await box.ShowDialog2(parent);

                    if (box.Result != MessageBox.Results.Yes)
                    {
                        return;
                    }
                }

                SaveFileDialog dialog = new SaveFileDialog() { Filters = new List<FileDialogFilter>() { new FileDialogFilter() { Name = "TIFF file", Extensions = new List<string>() { "tiff", "tif" } } }, Title = "Save tree plot" };

                string result = await dialog.ShowAsync(parent);

                if (!string.IsNullOrEmpty(result))
                {
                    ProgressWindow win = new ProgressWindow() { ProgressText = "Exporting TIFF image..." };

                    Task winTask = win.ShowDialog2(parent);

                    Exception exception = null;

                    await Task.Run(() =>
                    {

                        try
                        {
                            if (System.IO.File.Exists(result))
                            {
                                System.IO.File.Delete(result);
                            }

                            if (VectSharp.FontFamily.DefaultFontLibrary is SimpleFontLibrary library)
                            {
                                foreach (KeyValuePair<string, Attachment> att in parent.StateData.Attachments)
                                {
                                    AttachmentFontFamily fntF = att.Value.GetFontFamily();

                                    if (fntF != null)
                                    {
                                        library.Add(fntF);
                                    }
                                }
                            }

                            VectSharp.Page pag = parent.RenderPlotToPage();

                            if (cropRegionBox.SelectedIndex > 0)
                            {
                                pag = ApplyCrop(pag, cropRegionRects[cropRegionBox.SelectedIndex - 1], parent.PlotOrigin);
                            }

                            pag.Background = pag.Background.WithAlpha(1.0);

                            double scale = widthPxNud.Value / pag.Width;

                            SixLabors.ImageSharp.Image image;

                            using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
                            {
                                pag.SaveAsPNG(ms, scale);

                                ms.Seek(0, System.IO.SeekOrigin.Begin);

                                image = SixLabors.ImageSharp.Image<SixLabors.ImageSharp.PixelFormats.Rgba32>.Load(ms);
                            }

                            image.Metadata.ResolutionUnits = SixLabors.ImageSharp.Metadata.PixelResolutionUnit.PixelsPerInch;
                            image.Metadata.HorizontalResolution = resolutionNud.Value;
                            image.Metadata.VerticalResolution = resolutionNud.Value;

                            SixLabors.ImageSharp.Metadata.Profiles.Exif.ExifProfile profile = new SixLabors.ImageSharp.Metadata.Profiles.Exif.ExifProfile();
                            profile.SetValue(SixLabors.ImageSharp.Metadata.Profiles.Exif.ExifTag.Software, "TreeViewer");

                            image.Frames.RootFrame.Metadata.ExifProfile = profile;

                            SixLabors.ImageSharp.ImageExtensions.SaveAsTiff(image, result, new SixLabors.ImageSharp.Formats.Tiff.TiffEncoder() { Compression = SixLabors.ImageSharp.Formats.Tiff.Constants.TiffCompression.Lzw });
                        }
                        catch (Exception ex)
                        {
                            exception = ex;
                        }
                    });

                    win.Close();
                    await winTask;

                    if (exception != null)
                    {
                        await new MessageBox("Error!", "Error while saving the TIFF file:\n" + exception.Message).ShowDialog2(parent);
                    }
                    else
                    {
                        this.FindAncestorOfType<RibbonFilePage>().Close();
                    }
                }
            };

            return mainContainer;
        }

        private static bool IsAlive(string guid, MainWindow parent)
        {
            foreach (Dictionary<string, object> dict in parent.PlottingParameters)
            {
                if (dict.TryGetValue(Modules.ModuleIDKey, out object moduleIdObject) && moduleIdObject is string moduleId && moduleId == guid)
                {
                    return true;
                }
            }

            return false;
        }
    }
}
